Architecture.md
matklad.github.io
matklad.github.io
(The filename on disk is literally "architecture.md" -- it is a Markdown file rendered by Caddy's template handler: https://github.com/caddyserver/website/blob/master/src/docs/...)
It could use some improvement, but it's been really great for helping people learn how Caddy 2 works at a high level. Beyond our docs, I always encourage new contributors to thoroughly explore the godoc and code: it's very well-commented and organized, especially once you know how it all comes together. A single document will never be sufficient. But it can help you map between concepts and code.
Code search is also invaluable for this; I recommend Sourcegraph: https://sourcegraph.com/github.com/caddyserver/caddy
Edit: One other valuable piece is explaining why the architecture is the way it is. Our architecture.md doc links to a video that explains how I arrived at Caddy 2's architecture (and why it's not arbitrary): https://www.youtube.com/watch?v=EhJO8giOqQs
Typically I try to start with tiers (1, 2, n-tiers...?) that show how the system might be deployed. I then list list layers (user/facade/business/data), interfaces between these layers, and components within each layer. I do that for each tier.
After that maybe something about quality objectives and how they might be met, eg: availability (MTTF / (MTTF + MTTR) * 100), efficiency, flexibility, integrity, interop, usability and so on.
This leads to a physical delpoyment model, which shows layers deployed to tiers. And yes, I'm rather fond of Visio.
Then a bit about approach (dev/deployment and operational management) risk and stakeholder management, technical reviews (change control board maybe?), and project reviews.
To be fair I come from a predominantly critical systems world (telecoms OSS and BSS, healthcare, transport and some fintech. And in that world architecture is very far removed from actual code until eventually.
What I want to know is, what are the key performance considerations, failure modes, recovery procedures, etc.
People don't consider using my project. There's a client with a business problem, there's a vendor who solves problems for clients. The vendor produces an architecture document that describes how technology will achieve a solution †.
There is no noping the fuck out, as this is a hospital asking you for a one-off to manage/settle insurance payments. Or an electoral district asking you to merge three emergency response systems into one. Or Nokia asking you to tariff calls going through a switch in real time.
† This is nuanced. Often a client's procurement department invites a number of candidate vendors to submit proposals including a design proposal/architecture and associated cost estimates. Vendors range from the high end (McKinsey, Bain & Co, Ernst & Young) to the mid-tier (Wipro, Accenture and so on) to the niche.
I’m well aware of what it is. I’ve been on real time telecom stuff (your last example) and know for a fact that engineers nope the fuck out of these huge ass architecture documents that include stakeholders, change control, etc.
Inevitably there is some kind of outage or botched upgrade with lots of finger pointing and then the vendor covers their ass by referencing “page 248 under heading ‘assumptions about bisectional bandwidth’” or some bullshit right before the section on ‘renegotiating requirements during a government declared emergency’.
There needs to be a better way because I assure you, the people using your system are (on average) barely going to skim your document.
Being a “very serious” industry does not change this. Look at the disaster that was Healthcare.gov. That had mountains of documents like you describe they overlooked the simple requirement of scaling identity lookups.
By way of example - here's a conceptual design for an insurer wishing to participate in a panel of protection providers.
https://www.wittenburg.co.uk/images/consulting/conceptual_de...
If you're in this world all of it is self-evident, none of it a surprise, and you understand the flow. That document leads to other more detailed designs that each address one distinct rectangle in that image.
At some point all of this will become a specification. Again the immensity of that depends on the ability of the authors. On their ability to manage complexity, and their skill as technical writers.
Either way yes, when the wheels fall off that document becomes the truth, the one everyone goes to to settle disputes. And with a capable team it never gets to that because the documentation is succint and agreed to, and what is delivered matches what was specified.
OMG you worked on that? The blame for this one (and Universal Credit) sits with GDS. Their approach at the time was agile or die, and then try to develop an enterprise system like they did the Guardian's CMS (GDS was staffed initally by ex-Guardian devs).
I had the misfortune of reviewing their Government Gateway replacement, which at the time focused singularly on identity federation. GDS was hugely anti-Microsoft, who built the Govrernment Gateway. And so they chose the SAML protocol over the incumbent ADFS, breaking every dependency.
GDS was way out of its league. An inexperienced 25y/o will draft exactly the kind of architecture document you describe.
I feel for you, and I now understand the cyncism.
https://www.wittenburg.co.uk/Work/Books.aspx
The one that made the biggest impact on my approach to architecture is "Designing Solutions for Your Business Problems: A Structured Process for Managers and Consultants" by Betty Vandenbosch.
The one that made the biggest impact on me in ever is "Wicked Problems, Righteous Solutions" by Peter DeGrace and Leslie Hulet Stahl. Lots of forehead slapping and kicking myself ensued. Even more applicable today than it was in 1990 when written.
"Peopleware: Productive Projects and Teams" by Tom DeMarco and Timothy Lister comes as a very close second.
Not directly architecture or technology-related is "The Story Factor by Anette Simmons". It... changes you. Really useful for technical writing.
Also i read your home > work articles
They are excellent
Displaying the folder/file structure and explaining what is what is a must. An example from Diem[1]:
consensus
├── src
│ ├── block_storage # In-memory storage of blocks and related data structures
│ ├── consensusdb # Database interaction to persist consensus data for safety and liveness
│ ├── liveness # RoundState, proposer, and other liveness related code
│ └── test_utils # Mock implementations that are used for testing only
└── consensus-types # Consensus data types (i.e. quorum certificates)
└── safety-rules # Safety (voting) rules
I recently digged into dependabot's code, and I found it extremely well structured. For example you have an Architecture section in the first README[2] with a diagram (how awesome is that!) and with links to the README of all the sub components, which themselves live in the subfolders[3].What I dread the most is going through a new codebase and not seeing any documentation in internal packages. Like how the fuck am I supposed to understand anything in there? By reading all the code?
[1]: https://github.com/diem/diem/tree/master/consensus#how-is-th...
[2]: https://github.com/dependabot/dependabot-core#architecture
[3]: https://github.com/dependabot/dependabot-core/blob/main/comm...
This is one of the superpowers of Go: for most Go projects, this is exactly what I'd do. Just read the code. It's easy to follow, it's all formatted the same, very little implicit behavior, and I don't need an IDE to do it.
Few languages were designed to be read by others. Thankfully Go is one of them.
What optimizations have you tried that failed? What 3rd party tool used to be integrated but now isn’t because the maintenance burden was too high? Why is it safe to ignore this exception?
Actually I find it quite implicit the way Go structures packages. You definitely need an IDE (I use neovim + coc.nvim) to jump to a definition unless you want to grep the folder for where a struct is defined...
But that's it I believe, if it's in a different package then you'd have to explicitly write the import
But still, you can't replace documentation.
Many years later, I still believe that's how it should be.
I think Github should really start taking a stance on convention, and come up with project structure conventions that would help project navigation. For example, youtube does that with chapters in videos, if you write a description with timestamps it will display these chapters in the timeline of the video[1].
IMO Github should do the same if you have a README in your folder, describing the folder with a certain syntax, then it should extract this info and show it next to folders.
If anyone at Github is reading this, pretty please :D?
e.g. something very simple would probably work like the first line found that matches something like:
[Ignore_Non_Words]Summary[IgnorePunctuation&Spaces](Capture)It would encourage people to write folder-level readmes if nothing else.
So for instance, if save_parser.py starts with
""" Parse content loaded from save files
...
"""
and math_backend.rs starts with //! List of data structures necessary for maths operations
//!
//! ...
then your source tree UX should look like: src
├── save_parser.py # Parse content loaded from save files
├── math_backend.rs # List of data structures necessary for maths operationsOne can still link to those from the global README.md
Documentation in the source is different from a directory organization documentation IMO. Rust has a lot of tools to write good documentation in source, and it is here to help produce good documentation for users of a library, not to explain how to contribute to the code or understand how everything is architected.
Of course one can see the files. But I rarely deal with "files" in my IDE, I deal with packages/namespaces/modules/functions/classes/methods. The natural place to put architecture documentation would therefore be the top-level code unit (e.g. package/module/namespace), to which one can easily navigate when viewing docs.
> Documentation in the source is different from a directory organization documentation.
Is it, given that directories tend to be organized around code units (again: packages/namespaces/modules/classes)? It is thinkable to store code in databases (see Dylan's IDE, either implemented or it was on the roadmap, I don't remember) and all the architecture documentation not directly attached to semantic units of your code would be lost.
See https://github.com/redis/redis/blob/unstable/src/ziplist.c for an example.
(And Rust/Golang have the best documentation system out there of any language I have ever seen).
For example, documentation in Golang does not follow your file structure, it just pulls up any public API comment and displays it. It makes sense, why would I care about how these functionalities are implemented and architected as a user?
Second point: a system has different types of languages and ways to organize things, so it’s not always clear where to look for doc. Whereas a README is always obvious.
brew tree (if you are on Mac)
Example: https://github.com/cl-test-grid/cl-test-grid#system-architec...
On every level I first explain the task solved by this module and then go into the implementation details. Knowing the purpose first helps reader to understand impl details.
(The other significant factor is running tests.)
1. Spend a long time writing the doc.
2. Wait for a person to chance upon it.
3. Hope you anticipated their questions.
It seems like the most important thing a person can do is reverse this: 1. Say who the doc is for.
2. Find that person. Ask them to try a lil contribution.
3. Frantically write / revise the doc.
IMO it's a lot like creating a presentation. The earlier the feedback the better!I love coding so much, and find it really hard to express ideas in natural language, so that in the end documentation... doesn't happen as much as it should.
What I find that really helps is the following:
1. Write down architecture specs (with interface specs etc), before coding. Not bloated, but really minimalistic. 2. Review these ideas with peers. 3. Happy coding and refine the docs.
Of course, update accordingly whenever you find yourself in a discussion about something with a contributor (no matter if the architecture doc is even part of the discussion or not).
If I have written down architecture notes in the first place, they are very helpful at this point; and if I haven't, it's a good time to start because I'll be acutely aware of the non-obvious parts as I re-familiarize myself with the code.
Apart from answering them, I end up converting 15% - 20% of questions into some revised content of my docs.
Doing documentation well, especially if you're working on brand new tech, is very frustrating and difficult. You're often moving too fast to find the time to retroactively update documentation, and you're right back into the viscous cycle of it constantly being out of date. I don't know what the solution is.
I know I will sound another Rust evangelist, but people this person is the main maintainer of RA (Rust-analyzer), a LSP protocol implementation, anyone who tried RLS (Rust Language Server) then RA knows how great this tool helps you at learning and developing stuff with Rust. I use the nightly version (which updates everyday) and ohh boy... Never had the "opportunity" to caught a nasty bug or anything.
Also read his blog, it's a joy :)
Ty Matklad <3
This feels about right to me. Not sure a single doc will help solve that, but even if it cuts the time from 10x to 7x or 5x, it feels worth it.
TL;DR: ADR's are a design choice for a lightweight process to store and manage the history over what architecture decisions have been made in the past and why. They should be tracked within git so that the history of decisions and how these evolved is provided for free. Just track all this within an `adr/` subdirectory at the root of each project.
"Communicating and documenting architectural decisions" - David Ayers LeadDevNewYork(2019): https://www.youtube.com/watch?v=rwfXkSjFhzc
Mostly, think about what someone reading this in 6-24 months will think. Without some background reasoning, parts may seem over-engineered and unnecessarily complex. Say the same time, no one will care about the hours of debate that went on, or the shortcomings of the v1 prototype this replaced.
I’ve adopted ADRs and it has been good for decision making, but it doesn’t help much new contributors to find exactly what module they need to focus on for a task.
Also the author: "A good example of ARCHITECTURE document is the one from rust-analyzer" => redirects to an architecture file that takes 32 whole smartphone screen scrolls to read
1. put a one line comment at the top of each code file.
2. The build extracts that comment and dynamically updates a readme.md for the directory that describes each code file.
3. The build also produces a master document that includes the same summaries in a single document.
This provides a light indication of what each code file is and how they are organized into directories. It doesn’t provide any indication of flow control or any kind of logical linking.
Since the documentation is prepared via automation you can add new files or delete files and the documentation remains current. If such a comment is missing from a code file the build fails with error messaging. Keeping each comment up to date is still manual though.
Do you have an example repo?
Pic is a language Brian Kernighan created for troff toolchain. It's very versatile and has macros, even! Manual: https://pikchr.org/home/uv/pic.pdf
Contemporary implementations (pic2plot, dpic, pikchr) can output SVG so the results look very nice and are repository-friendly and diff-friendly to some extent.
I've bolted it to Emacs for "interactive" editing: https://iki.fi/newcup/cons-trained/2021-02-03-neat,-simple-v...
If anyone knows of other open source projects that do this, it would be particularly useful.
* Initiators (things that receive, decode, and validate input)
* Controllers (Business logic containers. One function refers to one business action)
* Services (Used by controllers to effectuate commands. Services absolutely cannot call other services)
* Cross Cutting concerns (Common model objects, logging, top level error handling, etc)
This allows even a new person to pick up a project and orient themselves immediately.
First: we found that it creates a spider web of dependencies if we din't have this rule. Instead, if you return control to the controller after performing a discrete action, it makes sure that business logic stays out of services and keeps service functions short and directed.
Second: it makes it easy to keep the entire design in your head.
Third: It promotes composition. This leads to easier testability with mocks rather than having to resort to full blown integration tests for even the smallest things. (We still do integration tests, but mock tests can be churned out in volume and are less fragile).
In a big project, when I'm hunting for code that does something and I have no familiarity of the codebase, I want to be able to follow a chain of ARCHITECTURE documents from the root of the project to the implementation of the feature I'm looking for.
If your documentation is poor enough that I resort to finding a string used in the feature and grepping the whole source tree for it, then your codebase will be tricky for someone new to get started on.
At least having a single file means only one file potentially goes out of date, instead of dozens
An architecture document should be the code equivalent of a combined street map and tourist guide. Its purpose is to bring strangers up to a minimum level of familiarity with the code as quickly as possible. That includes where things are, why it was architected this way, things to look out for, and a few interesting points of weirdness perhaps.
I work on a diagram maker that syncs with a Github repo. So whenever you make changes to this architecture diagram, it'll push changes to the repo, with screenshots directly in the README (turning the repo into a diagram presentation).
This also allows people to colocate the diagrams alongside the code or docs by including the synced repo as a submodule.
See Markdown Preview Enhanced: https://marketplace.visualstudio.com/items?itemName=shd101wy...
Or add Mermaid to the built-in markdown preview: https://marketplace.visualstudio.com/items?itemName=bierner....
You can also edit and enjoy these markdown pages with diagrams on Mac, iPad, iPhone: https://www.mweb.im/
Lastly, if anyone is not sure what to diagram for readers of architecture.md, consider the C4 model:
That's what I use draw.io aka diagrams.net for. I made an extension that helps to create markdown-embeddable/versionable draw.io diagrams in vscode: https://github.com/hediet/vscode-drawio (there is also an IntelliJ extension)
I have to admit that I sometimes struggle with pixel perfect layouts though and rearranging nodes is time intensive.
However, most text based systems don't have a nice github integration and rely on manual export, which you can avoid when you diagram is not a plain text file but a rich diagram editor.
Still, those can drift from the actual implementation to the point where they are both misleading and confusing. Such is the entropic nature of software.
Yes, that’s extra thick sarcasm.
In practice, I personally didn’t find it difficult to maintain half-decent ARCHITECTURE.md, and I am not at all good with keeping the docs otherwise.
This is not actually possible. You're kidding yourself if you think such a document is "future proof."
This seems pretty easily fixed by appropriate linting which includes link checking. Perhaps it's overkill, but personally I like to validate anything I can automatically so I try to validate syntax and links in Markdown documents in my projects.
I had a client with custom code shortly below 7k LOC.
Split into many different rules.
To make a long story short: Now that I know the map (the architecture) I can find my way quite fast and am currently in the process of simplifying things as far as possible.
It's also what I implemented for my project archivy (https://archivy.github.io/reference)
.....although I can be as sloppy as the next person at keeping documentation up to date which is why I think even a high level design and notes is better than nothing.
[1] https://github.com/google/stenographer/blob/master/DESIGN.md
Write simply and clearly.
Too much verbosity and detail is difficult to follow. That's what the code is for. It is almost a superpower to be able to write succinctly and clearly. This isn't some contest to showoff your deep knowledge of a particular niche. I've seen developers get 'nerdy' with their docs for a lack of a better term.
One trick here is to keep in the source, every sentence on a separate line. That way, it's hard to miss complicated sentences. As a bonus, docs become easier to diff.
rust-analyzer does this: https://raw.githubusercontent.com/rust-analyzer/rust-analyze...
I guess it’s a nice touch, especially for personal projects that get abandoned and you need to refresh your state of mind after X amount of months/years.
> Additionally, the shorter it is, the less likely it will be invalidated by some future change. This is the main rule of thumb for ARCHITECTURE — specify only things which are unlikely to frequently change. Don’t try to keep it synchronized with code. Instead, revisit it a couple of times a year.
An interesting systemsy difference is that missing style.md means more work for maintainers (as they need to do more cleanup), while missing architecture.md means more work for contributors.
Way too often I find this document missing. Especially in the rust ecosystem: I see some crate bumped their version number, but no info about what changed. (And no, a git log is not a good substitute)
[0] https://github.com/laurent22/joplin/tree/dev/readme/spec
I don't mind images, just wondering if Github would make it easier to generate those. Something like the syntax of web-sequence/uml but not limited to data-flows and something more simpler, ideally.
* https://monodraw.helftone.com/
From my experience, you don't want to add anything too complicated or anything that's volatile to code, but in some cases a high level overview of how bits of an application fit together can be handy. These days it might be a better idea to just embed or reference a PlantUML diagram instead.
Then I use the markdown-images package [2] for Sublime Text, and I can see those png images in my markdown files in the editor as well.
Benefits of this approach:
* Powerful diagram editor, free to use.
* Editable diagram and embedable image in one file.
* Diagrams rendered in text editor.
It makes it so much more likely that I'll contribute with a proper PR. There's some codebases I would have liked to contribute to but they were so complicated and with so few comments I just gave up and only reported the bug / made a feature request. Even worse when they have complicated undocumented build systems.
https://github.com/outline/outline/blob/main/ARCHITECTURE.md
FWIW, this is generally false: https://graydon2.dreamwidth.org/193447.html
Great advice though, otherwise.
And yes, I myself am struggling with ragged right. I really wish to have proper text hyphenation and justification, to have a book-like feel. Sadly, justification without hyphenation looks ugly, and `hyphens: auto` doesn’t work well (and wasn’t supported in chrome last time I checked).
I am pretty baffled that such basic (in terms of impact, not in terms of complexity of implementation) feature isn’t widely available.
Although I am not a web designer, so I might be missing some simple way to solve this.
The web would look much more beautiful with proper justification. It's a pity technology is not there yet. TeX solved the problem for DVI/PDFs long ago, so maybe we should start blogging on PDFs!
It can sort out alot of these issues that have todo with clearly articulating how complex software works.
What happens past the 200k mark?
Realistically, past the 200k lines of code point you aren't dealing with a codebase anymore; you're an _organization_. You need knowledge management--where do architecture decisions live, how are they approved, how are they taught to new developers, how are they updated as maintainers come and go, etc. It takes strong engineering management and leadership to keep it together.
> Nothing in the model layer specifically doesn’t depend on the views.
is there a typo or this is intentional?
It'd be great if there were FORMAL ways * to demarcate that are standard (yeah, what XKCD said) in ALL languages. Then anyone could just run a 'show architecture' application on the code and it would always be current.
After a few months (or thousands of loc), that map in your head starts to fade.
* EDIT: call it, say, CAML
What a good idea.