Writing enterprise software: notes to the self
Most recently updated: August 4, 2023.
Below I set out rules that help me write enterprise software faster. The rules mentioned are based on 20+ years of experience and dozens of programming books. Albeit decades of experience, I still learn every day. Hence, I will keep updating this article every so often.
Mark, save, or bookmark this document and quickly scan it every time you start a new project.
Rule: Always think of architecture
Reason: All code has an architecture, even if you didn’t think about it. Your architecture can either be good, and result in easily adjustable code, or your architecture can be bad, leading to code becoming ever more complicated to change.
The best architectures are layered. There are two types of layered architectures: horizontal-sliced architecture and vertical-sliced architecture.
Horizontally sliced architecture
In a horizontal-sliced architecture you separate the domain, application, infrastructure (databases, queues, etc.), and the UI logic from each other.
In a layered architecture code is separated from each other using dependency inversion.
A huge plus of using a horizontal-sliced architecture and dependency inversion, is that the code in the application layer and the code in the domain layer are easily unit tested.
Examples of well engineered layered architectures: onion architecture, ports & adapters, CQRS …
Vertical sliced architecture
In a vertical-sliced architecture, the focus is on delivering end-to-end functionality for a specific feature. This approach emphasizes the integration of all the required components and layers needed to deliver a single feature, from the user interface down to the infrastructure layer. This architecture is also called “feature-based” or “feature-driven” architecture.
Whether you choose a horizontal or vertical sliced architecture could depend on your tech stack and your team capabilities. If your team consists mostly of full-stack devs and your tech stack allows it, I’d advise you to start with a vertical sliced architecture.
Rule: Never ever create something that you don’t need right now
Reason: when implementing a new piece of software I’m not only considering the current use case but I’m also thinking about potentially other use cases in which that piece of software could be used. This distracts me from actually implementing the feature as fast as possible. To help myself focus on the current use case, I think about the following:
“What if this feature does not get implemented as soon as possible? Answer: we go bankrupt.”
Although this seems absurd, it’s true. If you’re too slow implementing new features, competitors will put you out of business.
Rule: First solve the happy path
Reason: When implementing a new use case, keep it as simple as possible. First solve for one very specific case. Do not consider edge cases. Do not consider polymorphism, scalability, performance or anything else. Your only task is to implement the happy path.
This rule is related to the rule above, but it’s so important that I turn this into a separate rule. Often I find myself thinking about polymorphism, scalability, and other details when creating a use case.
Rule: Use test driven development
Reason: Use test driven development, i.e. write your tests before creating the actual implementation. Your tests first fail as the actual implementation is missing. Then you code up the implementation, and run your tests. Your test should then pass. I often find myself writing the implementation first, then testing the implementation.
The major benefit of writing your tests first is that they help you think about the code that you are going to write in the way somebody is going to use your code. Simple google Test Driven Development to learn more about the benefits of writing test first.
Note: don’t strive for a 100% test coverage.
Rule: Use frameworks in the infrastructure layer
Reason: Frameworks provide a lot of utility functions and perform a lot of the heavy plumbing for you. However, be careful to use frameworks throughout your entire application. Before you know it, you are married to a framework. Subsequently, if a framework develops in a direction that is not suited for your needs, or if the maintainers of the framework abandon the project, you may need to rewrite your entire application.
Use frameworks to help you create APIs, perform authentication, perform messaging, and perform other input/output. Do not write code that handles this but find a good framework instead.
By keeping the use of the framework restricted to the infrastructure layer, you can easily change frameworks.
Rule: Care about changes when changes are required
Reason: Good code is code that is easy to change. Hence, making sure your code is easy to change is critical. However, don’t overthink it. When you already use a layered architecture you already apply the SOLID principles without knowing it. Applying the SOLID principles makes your code already easy to change. The layered architecture does most of the heavy lifting for you.
Example: How a layered architecture makes your code easy to change
Imagine you depend on a third party API and that API changes, then those changes should be contained in your infrastructure layer. Higher layers should not be affected by the change. Simple, create a transformer in the infrastructure layer and all should be well.
Rule: Make your code easy to read
Reason: code that is easy to read is easy to change. Whenever you see yourself writing a comment, think about renaming the variable(s) or function you are using. If your doing a calculation, pre-defining all variables in easy to read names help! Split a calculation or function into multiple steps, where the result of each step is either a variable name or a function name that describes the results or describe what the function does.
Rule: If you touch it, make it better
Reason: if you find it hard to understand a piece of code then you have to put in a lot of effort to understand it. When you do, make sure to refactor the code such that it is easy to understand. That way, the next time a developer needs to change it, that developer will need to put in less time to change the code. Often such a refactor can be done in a matter of minutes. Re-understading often takes a lot longer. In addition, it makes developer anxious to make changes to the existing code. They might end up structuring around the difficult code by writing adapters. Sometimes that is a good idea even if the code is easy to read. But difficulty to understand the code should never be a reason to do so.
Rule: Be consistent
Reason: consistancy is key in understanding a code base. It makes code faster to find and easier to understand.
So, put similar code in similar places (e.g. if you perform input validation in the controller, make sure to always call it in the controller and not all of a sudden in the infrastructure layer). Make sure everyone understands which code belongs in which layer.
In addition, don’t use synonyms.
Rule: Use bounded-contexts to structure code
Reason: Bounded-context make it easier to talk with the business about features. In addition, if you structure your code around bounded contexts its easier to find code, and to break-up code into microservices (may the need arise). In addition, make sure to structure teams around bounded-contexts.
Search the internet for the words: “ubiquitous language”. I highly advice the book: Domain Driven Design by Eric Evans.
Rule: Use names that align with words used by the business
Reason: when talking about features with the business, make sure that everyone (developers and the people working in the business) has the same definition for the words used in conversations. If you encounter a confusion of tongues in a conversation, then ask “what do you mean by ….?”. It’s better to ask this question too often, then too few. Don’t be afraid to come across as dumb!
Sometimes, words in the business and words in IT sound similar. For example: “pod” and “bot”. If you encounter words that sound similar and you often need to use these words in a conversation, it helps too enrich the business’ vocabulary and introduce a new word(s) to replace one (or both) of the words. Ideally this new word is then in the code base and by the business. Where the latter might be hard to realise.
This document is work-in-progress. The rules mentioned are based on 20+ years of experience, but I try to learn more and become better every single day! Do you miss a rule that helped you write better software? Share your knowledge and leave a comment.