Engineering Lessons

  • Focus on the big win. When there's a big win, it makes the work 1) rewarding, 2) keeps you focused on what's important, and 3) encourages you when you encounter challenges.
  • Think about evolve-ability of your APIs. If you make a mistake, which is inevitable sometimes, how hard will it be to fix the mistake? Days, weeks, months or never?
  • Compatibility over purity. It's almost always better to be pragmatic and pick something that's compatible with existing patterns than it is to focus on purity.
  • We are farmers, not builders. Even though engineers like to think of ourselves as building new features and systems, in reality most work is about maintaining and cultivating existing systems which inherently involves messiness with real-world systems.
  • "Plans are worthless, but planning is everything". Dwight Eisenhower said this quote and for engineering, I think it holds true. Even though designs will always evolve over time, it's still worth thinking about design up-front rather than letting it be a sprawling architecture.
  • API: Principle of least surprise. Even without reading documentation, users of your API should not be surprised by the outcome of your API.
  • API: when in doubt, leave it out. When you're unsure about whether an API is needed or whether the current design is sufficient, it's usually better to do nothing. Doing nothing gives you the flexibility to do the right thing later.
  • API: Don't let implementation details leak out. For example, if you have a database interface, you don't want the underlying implementation throw a SQL error. Make sure your exceptions match the same level of abstraction (Joshua Bloch).
  • API: You can’t make everyone happy but you shouldn’t make everyone unhappy with your changes.
  • Documentation is critical for widely-used API. Without excellent documentation, it's highly unlikely anyone will want to use your API and even if they do, it's even more unlikely they will be able to correctly use your API.
  • Implementation: keep it simple silly. When you're unsure whether an abstraction is beneficial, avoid it. Do the simple, obvious thing first like copy and paste code. When you see the pattern emerge, then abstract.
  • Inheritance is not primarily for code re-use. Inheritance is for creating a hierarchy of classes. If you don't want a hierarchy, then use composition instead. This is why people say "favor composition over inheritance". It's not that inheritance is inherently bad, but when you see deep hierarchies with lots of incoherent overrides in some of the chains, then it's a sign that inheritance was mis-used.
  • Good tests is mostly about testability. There's no trick to writing really great tests if the underlying production code is written in a very non-testable manner.
  • Refactor the part that has debt & is changing. A lot of people do code cleanups for parts with technical debt without assessing whether the refactoring effort is worthwhile. If a component is stable, it's usually not worth refactoring. Don't refactor for the sake of refactoring.
  • Understand the context for a tool. Tools like languages, frameworks, libraries, etc. almost always have a particular context for which they are suitable. Understand your own context and understand which contexts are suitable for the tool.
  • Keep functions pure. This means side-effect free. Don’t mutate global variables, parameters, etc. A pure function has no observable side-effect (e.g. an internal cache might cause a side effect, but this isn't observable from the caller).
  • Don't underestimate how hard change is. No one likes change, particularly when it's small but noticeable and has no obvious improvements from before.


views