Friday, June 1, 2018

Good Defaults for Technical Decisions

In my experience as a software engineer I've found a few "rules of thumb" for technical decisions. None of these are hard requirements or things that can never be false. However these are good guidelines if you don't have a reason to make a different decision. Unlike most engineering decisions which first present the constraints and then try and find a solution within them, this attempts to document decisions one should make if you didn't have any constraints in the first place.
Its possible you'll disagree with me on some of these and I'd like to understand why. That said, I'm not interested in specific projects where these are a bad idea but for an understanding about why these shouldn't be the default.
  1. Be explicit about your requirements. Don't automatically detect features, dependencies, or environment related issues. It is easier to change this later to make things more "magical" than go back and figure out what you need.
  2. Namespaces are good: even if you think you'll only ever need one. Its easier to modify in the future, versioning, etc.
  3. Errors ought to error. Warning ought to error or not exist. It is generally unhelpful to have noise in your output that you do nothing with. If a warning isn't meaningful, disable it.
  4. Keep scope local and private. Prefer hiding functions and information from the outside unless you have to decided to make this an API.
  5. Naming your first version as a "v1" and label it as such. During rewrites, migrations, or related issues prefer versions rather than names such as "next" or "new". There will likely be many "nexts" and only one v2.
  6. Structured is better than unstructured. Similar to the point about explicitness: it is easier to go from structured to unstructured than vice versa.
  7. Fixed is better than editable. Don't let people change things unless there is a reason to. This also applies to code (immutable is better than mutable).
  8. Don't rely on people not making mistakes. Even if you have perfect people, they might be tired, have something in their eye, misremember a fact, or otherwise be operating at a sub-optimal state.
  9. Name same things the same and different things differently. Use, and accept, the same formats for the same thing at all layers of the stack. As a counter example ruby outputs missing gems as name-version but gem(1) expects name:version.
This is a work in progress document and I'll try and update it over time

Saturday, February 17, 2018

Some rules for designing libc style APIs

  1. Identifiers should not have vowels; they are expensive and difficult to type.
  2. An identifier must not be longer than 8 characters. The only exception are functions intended for standardization like sched_ss_init_budget.
  3. Functions must not be reentrant. Relying on internal state means you can avoid allocating memory.
  4. Functions should take at least two parameters. The second parameter should be a "flags" parameter which causes the function to do entirely different things.
  5. Flags should be passed as macros with unspecified values. These macros must not have reasonable values.
  6. Error handling must be done in one of two ways. The choice must not be consistent with other functions in the library:
    1. The real return value should be stored in an "out" parameter. The return value must only determine if an error has occurred or not.
    2. If an error occurs, the return value must be undefined. The return value can't be safely used without checking for errors using a separate function (e.g., fgets).
  7. The error code should be in errno, requiring the setting of `errno = 0` beforehand and checking after an error occurs. However, the return value should be a value legally allowed to be in errno, so that initial attempts to use the function appear to work.
  8. If the function returns a string, it must do so by modifying a memory location given as a parameter. Whether or not the string is terminated with a null must be determined solely based on the length of the output, a user supplied parameter, and choice of compiler.
Thanks to jp, okdana for the inspiration and review; thanks to gonzo, arbrock for review.