| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- .. SPDX-License-Identifier: GPL-2.0
- bcachefs coding style
- =====================
- Good development is like gardening, and codebases are our gardens. Tend to them
- every day; look for little things that are out of place or in need of tidying.
- A little weeding here and there goes a long way; don't wait until things have
- spiraled out of control.
- Things don't always have to be perfect - nitpicking often does more harm than
- good. But appreciate beauty when you see it - and let people know.
- The code that you are afraid to touch is the code most in need of refactoring.
- A little organizing here and there goes a long way.
- Put real thought into how you organize things.
- Good code is readable code, where the structure is simple and leaves nowhere
- for bugs to hide.
- Assertions are one of our most important tools for writing reliable code. If in
- the course of writing a patchset you encounter a condition that shouldn't
- happen (and will have unpredictable or undefined behaviour if it does), or
- you're not sure if it can happen and not sure how to handle it yet - make it a
- BUG_ON(). Don't leave undefined or unspecified behavior lurking in the codebase.
- By the time you finish the patchset, you should understand better which
- assertions need to be handled and turned into checks with error paths, and
- which should be logically impossible. Leave the BUG_ON()s in for the ones which
- are logically impossible. (Or, make them debug mode assertions if they're
- expensive - but don't turn everything into a debug mode assertion, so that
- we're not stuck debugging undefined behaviour should it turn out that you were
- wrong).
- Assertions are documentation that can't go out of date. Good assertions are
- wonderful.
- Good assertions drastically and dramatically reduce the amount of testing
- required to shake out bugs.
- Good assertions are based on state, not logic. To write good assertions, you
- have to think about what the invariants on your state are.
- Good invariants and assertions will hold everywhere in your codebase. This
- means that you can run them in only a few places in the checked in version, but
- should you need to debug something that caused the assertion to fail, you can
- quickly shotgun them everywhere to find the codepath that broke the invariant.
- A good assertion checks something that the compiler could check for us, and
- elide - if we were working in a language with embedded correctness proofs that
- the compiler could check. This is something that exists today, but it'll likely
- still be a few decades before it comes to systems programming languages. But we
- can still incorporate that kind of thinking into our code and document the
- invariants with runtime checks - much like the way people working in
- dynamically typed languages may add type annotations, gradually making their
- code statically typed.
- Looking for ways to make your assertions simpler - and higher level - will
- often nudge you towards making the entire system simpler and more robust.
- Good code is code where you can poke around and see what it's doing -
- introspection. We can't debug anything if we can't see what's going on.
- Whenever we're debugging, and the solution isn't immediately obvious, if the
- issue is that we don't know where the issue is because we can't see what's
- going on - fix that first.
- We have the tools to make anything visible at runtime, efficiently - RCU and
- percpu data structures among them. Don't let things stay hidden.
- The most important tool for introspection is the humble pretty printer - in
- bcachefs, this means `*_to_text()` functions, which output to printbufs.
- Pretty printers are wonderful, because they compose and you can use them
- everywhere. Having functions to print whatever object you're working with will
- make your error messages much easier to write (therefore they will actually
- exist) and much more informative. And they can be used from sysfs/debugfs, as
- well as tracepoints.
- Runtime info and debugging tools should come with clear descriptions and
- labels, and good structure - we don't want files with a list of bare integers,
- like in procfs. Part of the job of the debugging tools is to educate users and
- new developers as to how the system works.
- Error messages should, whenever possible, tell you everything you need to debug
- the issue. It's worth putting effort into them.
- Tracepoints shouldn't be the first thing you reach for. They're an important
- tool, but always look for more immediate ways to make things visible. When we
- have to rely on tracing, we have to know which tracepoints we're looking for,
- and then we have to run the troublesome workload, and then we have to sift
- through logs. This is a lot of steps to go through when a user is hitting
- something, and if it's intermittent it may not even be possible.
- The humble counter is an incredibly useful tool. They're cheap and simple to
- use, and many complicated internal operations with lots of things that can
- behave weirdly (anything involving memory reclaim, for example) become
- shockingly easy to debug once you have counters on every distinct codepath.
- Persistent counters are even better.
- When debugging, try to get the most out of every bug you come across; don't
- rush to fix the initial issue. Look for things that will make related bugs
- easier the next time around - introspection, new assertions, better error
- messages, new debug tools, and do those first. Look for ways to make the system
- better behaved; often one bug will uncover several other bugs through
- downstream effects.
- Fix all that first, and then the original bug last - even if that means keeping
- a user waiting. They'll thank you in the long run, and when they understand
- what you're doing you'll be amazed at how patient they're happy to be. Users
- like to help - otherwise they wouldn't be reporting the bug in the first place.
- Talk to your users. Don't isolate yourself.
- Users notice all sorts of interesting things, and by just talking to them and
- interacting with them you can benefit from their experience.
- Spend time doing support and helpdesk stuff. Don't just write code - code isn't
- finished until it's being used trouble free.
- This will also motivate you to make your debugging tools as good as possible,
- and perhaps even your documentation, too. Like anything else in life, the more
- time you spend at it the better you'll get, and you the developer are the
- person most able to improve the tools to make debugging quick and easy.
- Be wary of how you take on and commit to big projects. Don't let development
- become product-manager focused. Often time an idea is a good one but needs to
- wait for its proper time - but you won't know if it's the proper time for an
- idea until you start writing code.
- Expect to throw a lot of things away, or leave them half finished for later.
- Nobody writes all perfect code that all gets shipped, and you'll be much more
- productive in the long run if you notice this early and shift to something
- else. The experience gained and lessons learned will be valuable for all the
- other work you do.
- But don't be afraid to tackle projects that require significant rework of
- existing code. Sometimes these can be the best projects, because they can lead
- us to make existing code more general, more flexible, more multipurpose and
- perhaps more robust. Just don't hesitate to abandon the idea if it looks like
- it's going to make a mess of things.
- Complicated features can often be done as a series of refactorings, with the
- final change that actually implements the feature as a quite small patch at the
- end. It's wonderful when this happens, especially when those refactorings are
- things that improve the codebase in their own right. When that happens there's
- much less risk of wasted effort if the feature you were going for doesn't work
- out.
- Always strive to work incrementally. Always strive to turn the big projects
- into little bite sized projects that can prove their own merits.
- Instead of always tackling those big projects, look for little things that
- will be useful, and make the big projects easier.
- The question of what's likely to be useful is where junior developers most
- often go astray - doing something because it seems like it'll be useful often
- leads to overengineering. Knowing what's useful comes from many years of
- experience, or talking with people who have that experience - or from simply
- reading lots of code and looking for common patterns and issues. Don't be
- afraid to throw things away and do something simpler.
- Talk about your ideas with your fellow developers; often times the best things
- come from relaxed conversations where people aren't afraid to say "what if?".
- Don't neglect your tools.
- The most important tools (besides the compiler and our text editor) are the
- tools we use for testing. The shortest possible edit/test/debug cycle is
- essential for working productively. We learn, gain experience, and discover the
- errors in our thinking by running our code and seeing what happens. If your
- time is being wasted because your tools are bad or too slow - don't accept it,
- fix it.
- Put effort into your documentation, commit messages, and code comments - but
- don't go overboard. A good commit message is wonderful - but if the information
- was important enough to go in a commit message, ask yourself if it would be
- even better as a code comment.
- A good code comment is wonderful, but even better is the comment that didn't
- need to exist because the code was so straightforward as to be obvious;
- organized into small clean and tidy modules, with clear and descriptive names
- for functions and variable, where every line of code has a clear purpose.
|