How to master a project's architecture and not base your decisions on assumptions

min read
Architecture is a cornerstone of every software product. The well-thought-out architecture allows you to expand and modify your software as you go. A rigid and misguided one does not. So how do you handle strategic decisions when you kick off a new project, and how do you stay on track? Learn Jimmy's approach.

Let's start with the word "agile" – what does it mean to you? Think about it for a minute. For us, it's a mindset applicable anywhere, not just in software development. It's a way of thinking. It's about making small steps frequently instead of a giant leap once at a time. You also might have heard about lean principles (The Toyota Way). That's complementary to agile. Both approaches work well together because they emphasize quality, speed, and waste elimination.

How to make the right structural decisions at the beginning? Start small

Data and information are super important when you start out. Gather everything you can about the project and look for circumstances that may affect the architecture. On the other hand, don't try to tackle every possible future scenario and overthink it. Balance is the key. Also, be careful not to fill the blank spots with your assumptions, and if you do – be ready to either confirm them or change them. Assumptions are often mistaken for facts, even though there is no real foundation behind them.

If you already have architecture blueprints and now you confront them with all the gathered data, do they still make sense? If not, but you still want to take this path, why?

Monolithic or microservice architecture?

This might be one of the most important decisions you'll make – simply put, there are two common approaches that you probably consider these days. The first one is monolithic architecture – a bit old school from today's fast-paced projects but still a good pick in many cases. The other is an architecture based on microservices. It's trendy, and it's advantageous when appropriately executed but can be a nightmare when done by an unskilled team. Which one do you choose?

Microservices are intended to solve particular problems, but as usual, they introduce new ones. Ones you might not have had to cope with in a monolithic application.

So which approach is more meaningful? In the beginning, monolithic applications with decent architecture are much more flexible and easier to understand, which is a great benefit. On the other hand, the chances are that you might need to isolate a specific part of the system at some point. Why? Probably due to performance and other factors, which is the moment when your first microservice might step in. That's completely alright, but developing microservices prematurely or relying solely on them might cost you a lot of trouble.

"If you can't build a monolith, what makes you think microservices are the answer?" Simon Brown

Therefore always build a very minimum, measure, and learn.


Keeping your domain logic decoupled and detached from the external, unstable environment helps you focus on the very core of your business and mitigate the fragility coming from external changes.

Imagine you find a great-looking external service that you'd like to integrate. Sure, why not, but make sure you set a precise boundary between your domain and the external service. Doing that gives you the flexibility that if the external tool doesn't suit you anymore, you can replace it without putting other parts of the system in jeopardy.

Another hot topic is frameworks. They tend to be quite opinionated and infiltrate your codebase. Sure, you might be 100 % certain that you will never migrate to a different framework but still — keeping your architecture decoupled from the framework allows you to test the domain and update the framework easily. With coupled architecture, a major update that brings along a new structure can be quite a hustle, and you want to avoid that.

Not to mention that external instruments are more or less out of your control, and even the most renowned tools sometimes disappear. Yes, it happens.


By outsourcing, I don't necessarily mean passing tasks to another development company (if you want to do that, make sure you choose a reliable one). I talk about leveraging existing solutions that can help you big time and are already out there.

Let's say you need to implement a feature that allows you to sell a book; it's a feature that will increase your revenue, but to sell a book, you need to deal with VATs which is complicated. That puts you in an uncomfortable position. Selling a book is your primary objective, but you can't achieve it without implementing another complex solution.

How great would it be if there were API services that already have the VAT logic implemented? Rest assured that there are plenty of them. Usually, when you deal with something, someone else has already solved it, and that's how you can quickly push your development forward. Integrate one of the services, hide it behind an interface (see Decouple) and start selling your books. And if you need to, introduce your own implementation of the VAT logic once you have it.

Measure everything

I already mentioned the importance of data and empiric knowledge, not just mere assumptions. Collecting data is an ongoing process, the more relevant data you have in your sleeve, the better. Track at least the most crucial aspects of your software, and don't overdo it. You can start with metrics like response time, request count, error rate and monitor resources like RAM, CPU. Always try to take advantage of the hosting platform you're using. It might supply you with excellent tools — e.g., take a look at Google StackDriver.

Always stress test your apps before you launch them. How to do that? Easily — create a replica of your production version and see what happens.

Push it over a cliff to find the cliff.

Thanks to the data, you recognize your borders and are able to confidently set up monitoring, which will let you know if you are getting close to the limits. Again, take advantage of tools that your hosting provider already has, you might find a treasure!

Watch, react and adapt

Now your software is running, you have set up the monitoring and collect feedback from users. Over its course, you will undoubtedly identify trends and probably trigger a few alerts that you are nearing the cliff. That's usually time to react, but sometimes it's not. Get a broader context first because it matters. Wasn't there an extraordinary load because a marketing campaign was running? If so and another won't happen anytime soon, then it still might not be a priority.

Look at the data more closely, isolate the problem and solve it. There might be different reasons for the same issue — is there an unoptimized algorithm? Or an SQL statement? Be careful what SQL ORM libraries produce. Or is it just a demanding process that takes time? If it's the last case, it might be a good opportunity for the microservice.

Turn assumptions into hypotheses

We just made our architecture decision based on data, and that's how it should be. But should you always passively wait until circumstances like a massive load push you towards new solutions? Not really. Take small steps but don't rebuild half of the app because of your assumption. Turn assumptions into hypotheses, build a very minimum, measure, and learn. Still not sure what's the difference between these two? Check out this explanation.

Continually evaluate and don't be scared of refactoring

Refactoring, the word that makes people uncomfortable. Why? I noticed over the years that this discomfort comes from the inability to continually evaluate the software, which results in sticking to the old obsolete patterns that will sooner or later hit their limits. That leads to more bugs, slower development, and a point of no return when you inevitably have to stop the development altogether and rewrite large parts of the code that can take weeks or months.

Of course, you should always try to avoid this situation. How do you do that? By continual refactoring, which will keep your codebase healthy and allow optimizations. Don't be afraid of refactoring, be frightened if it's not being done daily. Either you're a developer or a manager, emphasize quality. It's worth it.

Be pragmatic and focus on value

That's it. We went through several practices that allow you to focus on the value, maintain speed, eliminate waste, and make decisions based on data. I'd like to hear from you, whether you're an architect, a developer, or a manager. I believe all of you might find these practices relevant.

Remember that behind everything, there's just one core idea — pragmatism. Don't waste resources. Focus on the value.

Reach out to get more information.
Jakub, CTO

P.S. We also have great news! We are among the Top 30 iOS App development companies on DesignRush.

Scroll to the top