This article is continously updated
Abstraction
Do not create high levels of abstractions early on, the same way you don't build the full product with all features for your startup, but go with an MVP. You don't know what needs the future, and here we are talking about hours not years, will bring. Stay low level until you see patterns emerging. If an abstraction will save you time, implement it. And this has nothing to with a clean code architecture.
Interface and boundaries
Everything is an interface. Interfaces and clearly defined boundaries are the secret sauce to a scalable software architecture your future self will enjoy working in.
"Write modular code and monolith vs. microservice architecture becomes an implementation detail"
TDD
First you deny it, then you fear, then you try it, then you can't live without it. But it doesn't make sense to apply it before you don't understand software architecture well.
DDD
same here
- Most chaotic codebases get born because files get created in random locations, without any structure. In addition, those files contain functions that serve multiple purposes and handle data of various formats - no knowing which layer the actually belong to. Humans need structure, patterns - in order to be able to think and make sense of something. The larger and more interdependent a system gets, the more important that clear structure becomes. The lack of good architecture won't be noticable during the first days, weeks, sometimes even months. But then it bites. And uh, do those bites hurt. They hunt you at night, make you sleepless, force you to deepen your coffee addiction. It's rarely "just a simple project", an "MVP", a "PoC". Build solid, build for the future, save tears (there sure are moments where those are needed, and coding shouldn't be one of them).
Vertical sliced vs. horizontal architecture
Feature flags
- Feature branching etc
- Only makes sense with a good testing strategy and software architecture
Architecture > Technology Choices
Doesn't matter if you use GraphQL or Rest, when the underlying API is a mess. The other way around though, adding GraphQL to a well architected API, or switching from Node to Deno, is straightforward, because a good architecture is resilient to change in infrastructure.
Do not name Experiments "Test[...]"
Small thing I noticed even doing myself sometimes. Calling an experimental code (e.g. a React component) "TestXYZ", e.g. TestComponent
. It should rather be called ExperimentalComponent
Everything is an API ... or why you need incremental testing
...and you always develop an SDK for your future self or your team. Just the way of calling that API/SDK might differentiate: A normal function you import, a React Component, a REST or GraphQL API. They belong to different layers for sure, but might be re-used in all different places. So make sure to make them testable and mockable right from the start. -> Incremental Testing
Well-architectured monorepos
Monorepositories without layered architecture and a good testing strategy will turn into an uncontrollable spaghetti mess, which will feel like you just dumped your whole code into one giant file.
Some concepts to write about
- No library typesafe State Management for React
AI Project Ideas
Store and analyze code base abstractions - to develop new features; with enough abstractions the LLM can lookup (e.g. from a Vectore Database) everything relevant and develop whole features. We just need a documentation of all internal APIs and our design system, so the AI could potentially just write features on it's own.