The following story is horrifying, yet very familiar to any professional developer.
Chris the Client: How’s the app coming along? Are we ready to release it?
Dave the Developer: Great! All the features are working perfectly. And I’ve designed these classes to be small and reusable. It’ll be easy to maintain going forward.
Chris the Client: That’s fantastic! Let’s release this version 1 to the App Store.
Dave the Developer: Awesome!
Chris the Client: We’ve got some initial feedback from our users. And we want to add this new feature, and change this to that. How long will it take?
Dave the Developer: Hmm… If we’re going to do that, I’ll need to refactor this part of the app. It’ll take maybe a week.
Chris the Client: Why? I thought you said it would be easy to maintain.
Dave the Developer: Yeah, but your proposed changes will dramatically alter the way how things are working now. It’s not that simple.
Chris the Client: Okay, do whatever it takes. We want to get this out the door as soon as possible.
Dave the Developer: I’ll do my best.
Chris the Client: Some of our users are seeing crashes when they do this. Can you fix it?
Dave the Developer: Let me take a look… Ah, I found where the problem is. It’ll take a couple days to look into fixing it.
Chris the Client: Thanks.
Dave the Developer: This bug is really tricky. But it has been fixed. And I’ve released 1.1 to the App Store.
Chris the Client: Our users are still seeing crashes when they do this. I thought you said it was fixed!?
Dave the Developer: Yes, it was fixed. I’m not sure why it’s still happening. I can take a look.
Chris the Client: Yes, please do.
Dave the Developer: Oh, it’s not as simple as we thought. There is actually an edge case where…
Chris the Client: Okay, so how long will it take to fix it?
Dave the Developer: Maybe a couple more days.
Chris the Client: How’s the bugfix going? Some users also have this issue when they… By the way, we also want to add this new feature. We think it’ll…
Dave the Developer: It’s going okay. I think we found where it is happening. We’re still working on fixing it. Can you open a ticket for this new bug? I’ll take a look after this.
Chris the Client: Ok. What about this new feature?
Dave the Developer: Hmm… We’ll work on it after we fix this bug.
Chris the Client: Ok. Thanks.
Dave the Developer: Finally. The bug has been fixed. And we added this new feature as your requested. Version 1.2 is out now.
Chris the Client: Great. Thanks.
Dave the Developer: My pleasure!
Chris the Client: The new feature is causing this to happen. And now some users are seeing this problem where…
Dave the Developer: That’s weird. I have no idea why. But I can take a look again.
Chris the Client: Okay.
Dave the Developer: I think I found the problem. Because we changed this, it now causes that to happen. it is kind of related to the old bug. So that’s why we’re still seeing these crashes. Also the new feature is doing this. But it’s weird. It doesn’t always happen. Sometimes it’s fine.
Chris the Client: So what are we going to do about all these?
Dave the Developer: I think we should refactor this part and rewrite that part. It’ll make this work better.
Chris the Client: So, how long will it take?
Dave the Developer: Maybe a couple weeks.
Chris the Client: Hmm…
Long gone is the promised maintainability. The longer the story, the more painful it becomes. The saddest thing about this story is that it has no ending, as long as Chris the Client keeps paying the bills. If it ever ends, it’s going to be a messy divorce.
What’s going on here?
Dave the Developer has the best intention to design the app with maintainability in mind. Nonetheless, the app still ends up being hard to maintain. He feels bad to have to tell the client about all these delays. On the other hand, the client is mystified about why things always seem to take so long, and wonder if it was built the right way in the first place.
A good application architecture should be flexible
At the beginning of a new project, that’s when you know the least about your app. You won’t know all the classes it’ll end up having and the interactions between them. So, any upfront decision making is mostly futile, despite your best effort. Much like waterfall development, a full blown upfront design is a waste of time and effort. A design on paper never translates directly to working code. Details will emerge. Edge cases will reveal.
Does that mean we just dump everything in the view controller, let it become massive, and then deal with it later?
No. When you’re forced to deal with it, that’s when you least want to deal with it.
Some initial structure to the app is still desirable. You don’t want to overthink at this point. You just want to start from a sound foundation. For example, if you need to write some networking code, you wouldn’t just put everything in the view controller. Even without any experience, it’s just common sense to assume it’s best to extract it to some specialized classes. One class deals with talking to the API. Another deals with persisting data to the database. And so forth.
But an application architecture is more than just creating separate classes for each piece of functionalities. You’ve tried that multiple times in multiple projects. But it didn’t turn out too well, huh? What are you missing?
The problem is a lot of developers try to be perfect. They imagine a perfect story, perfect UML diagram, perfect classes, perfect design, perfect DRY, …etc. When something has to change that creates the slightest imperfection, they tinker with the code endlessly to try to get back to perfection. This process repeats itself. More changes leads to more imperfection leads to more time required leads to a bigger mess. It’s a badge of honor to work overtime and skip sleep to stay on this course of chasing perfection.
I once thought like that too, until I couldn’t keep up. Until I realized that’s not what we’re paid for. We aren’t paid for writing perfect code. We are paid for solving problems. Changes in requirements are simply new problems to solve.
This is a totally different mindset. And it’s difficult to adopt when you just begin. But once you get it, you won’t go back.
Now the question becomes: What is the architecture that allows me to solve new problems most effectively?
You should strive for a flexible architecture that allows you to make changes in the most painless way possible. In any development project, things are going to change soon or later. So it’s pointless to try to design the perfect architecture. That is a goal that you’ll never reach. Changes in requirements will render your perfect architecture less perfect every time. Instead, you want an architecture that aids you in making changes quickly and reliably, so that you can move on to the next problem.
Nothing stays perfect. Flexibility goes a long way.
I happened to find the Clean Architecture helps me do just that. Have you found yours?