Design
As you grow in seniority, you will be asked not only to implement designs decided by others, but to come up with your own design for increasingly complex features.
Good design is good, but hard
Good design has many benefits. They might be obvious, but they are worth repeating here:
- Cheaper in the long run: A quick fix might be a fast and easy solution now, but likely to lead to greater pain down the road; it just adds to the technical debt that will need to be refactored later.
- Fewer bugs
- Better extensibility: new features are easy to implement
However, good design is also very hard to achieve. It generally takes years of experience. Experience brings you:
- a larger "mental library" of design patterns; you've had more opportunities to see what works and what doesn't;
- a better-honed intuition, allowing you to reject outright bad design options, and be faster at reaching a better solution;
- a better ruleset with which to evaluate designs; you know what criteria to apply, and which ones carry more weight.
Criticise
Criticising being easier than making, I encourage you to do just that: criticise.
Whenever you see a piece of code, ask yourself whether you feel it's good or bad ("Does it smell?"). Try and explain why you think that. Do this for the code in tutorials, code from your colleagues in the project you are working on, eventually for your own code.
When your colleagues present their own design, try and poke holes in it. You'll be doing them a favour, by helping them hone their own skills (at coming up with a better design, and at defending their design proposals). You will also add value to the project by helping make a better design decision.
Practice
There is no precise point at which "design" comes into play. Even the simplest task requires some design thinking. Whatever your seniority, whatever the task at hand, ask yourself whether what you are doing is the best way. If you are responsible for the implementation, force yourself to come up with 2 solutions. Compare them and try and come up with the relevant criteria for deciding in favour of one or the other.
Over time, you will be given tasks that will require designing more and more complex features. Feel free to request such an assignment.
Requirements for good design
Calendar time
Nobody comes up with the perfect design in a rush (not all the time anyway). Even if working on your own, take the time to think it over. Wait a few hours before implementing it; or even wait overnight. Better still, a whole weekend.
This means of course, that you should try and start thinking about this design as early as you can.
Exhaustive understanding of requirements
If you don't know precisely and accurately what you have to do, you have zero chance of doing it right. If you don't understand the requriements, your design is more than likely to be suboptimal.
Don't assume that requirements are always perfect. Writing requirements is hard and time consuming. Most are incomplete, inconsistent if not downright contradictory.
Requirements are often filtered by go-betweens like a product manager, a project manager, the lead dev, a salesperson or relationship manager, who may have misunderstood the original request or put their own spin on it.
Even the customer may not always know exactly what they want. In some cases the customer will hide the complexity of the feature in order to "keep it simple". But for us to come up with a good simple design, we need to know the underlying complexity of the feature.
Frequently, a customer will claim not to be interested in one aspect of the feature; yet come back 3 months or 3 weeks later asking for precisely that!
So always read the requirements, reformulate them; ask for clarifications as early as possible. Identify as many edge cases as you can (Ask yourself "What can go wrong?"). Don't be afraid to ask the customer what happens in this or that case?
Put yourself in the user's shoes: would you want the app to behave as per the specs? Do this for all the users involved in the feature: end-user, admin, etc.
Warning
Feel free to make suggestions for changes, but never change the requirements without the approval of the product owner.
Discussion
Nobody comes up with perfect design on their own all the time. However, a discussion will almost always improve the design.
Explain your design to others; if you are alone on a project, find peers, or found your own peer group.
Listen to others' objections; try and defend your design. If you cannot come up with valid arguments in defence of your design, then maybe it needs to be revised.
How to evaluate a design
Below is a list of criteria which can be used to evaluate a design. The list is far from exhaustive. Just as important as the criteria themselves are the relative weights to be given to each. This is often more an art than a fixed rule.
- Does it meet all the requirements: A design that does not meet a single requirement is necessarily suboptimal (unless the set of requirements is impossible to achieve)
- Is it difficult to maintain? Generally, choose the design that is simpler to maintain
- Does it offer good potential for extensibility: Among the requirements, some will have a low priority, or a low probability of occurrence, or the customer is not sure whether they want them, or how. Does the design still offer some promise that these aspects will be easily implementable in the future?
- Conversely, does the design close off some avenues? Does choosing this design make it impossible in the future to do this or that?
- Will it be easy to maintain: Are there too many moving parts?
- Does it follow generally accepted design patterns: We should not follow or apply design patterns blindly. However, the right design pattern, suitable for the problem at hand, offers generally a tried-and-tested solution. Deviation should be justified.
- Loose coupling: Is the proposed design loosely or tightly coupled with the framework, another design decision, another part of the system, etc. The more loosely coupled the better.
- WIP
Documentation
Documentation as part of the design process
Design must be documented. Too often, it is not; later, it becomes difficult to understand why this particular design was chosen over another. Having access to documentation saves time as there is no need to go over the analysis again, and may help prevent errors: possibly that design was chosen for a very good reason that eludes us now.
Documentation must be part of the design process, and should start at the very beginning. Nobody likes writing docs after the fact; we'd all rather be coding. however, writing the documentation during the discussions and though process can be extermely helpful in formulating your own thoughts and sharing them usefully for others, allowing them to understand your proposed designm, and come up with useful question or objections.
ADRs
For documenting design, we use the ADR format. ADR stands for Architecture Decision Record. ADRS are simply a template for documenting design decisions. ADRs can be written in markdown format; they can be saved in whatever wiki or documentation platform used by the team (confluence, notion, etc.) or even saved as markdown files in the project's repo.
I encourage you to read this document to understand more about ADRs: Architecture Decision Records in Action.
The basic structure of an ADR is as follows:
# Short description of the issue as the title
## Summary
### Issue
Full description of the issue
### Decision
Description of the proposed design decision.
This should allow any person not involved in the decision or discussion to understand what to do (eg developer, chief architect, future members of team, etc.).
### Decision status
One of: Under discussion, Pending appproval, Rejected, Approved
## Details
This is a more free-form section. It should include all the information required to support the decision above: why it was made; what benefots and drawbacks were identified; what alternatives were considered?
A suggested strucutre could be:
### Assumptions
### Constraints
### Positions
### Arguments
### Implications
## Related
### Related decisions
### Related requirements
### Related artifacts
### Related principles
## Notes
The template above was taken from Joel Parker Henderson's repo, which has more examples available.