Collection of [[Things I believe (MOC)|Things I believe]] about [[Software engineering MOC|Software engineering]]. ## General principles - What > How: Build the right thing, *then* build it right. - Shipped is better than perfect - [[Keep it simple, stupid (KISS)|KISS]] and [[YAGNI]] trump [[Dont repeat yourself (DRY)|DRY]]. - Almost every decision you take has a trade-off. - Understanding a problem well is often more valuable than being the one who comes up with a solution for it. ## Technology selection & architecture - Adopt a [[Serverless-first mindset]] when designing a new (or extension to an existing) software system. - A service with none or poor [[Infrastructure-as-Code]] support is a big red flag. - [[Most new serverless apps should start as a monolith]]. - All dependencies should be one-way. - The benefits of using [[TypeScript]] over JS outweigh the overheads for most projects. - Think very carefully before considering writing your own internal framework. If you must, make it really tightly scoped. ## Development workflow - [[Code is a liability]], not an asset. Keep it to a minimum. - Enable fix-on-save in your IDE for linters/formatters. - All changes to code repository should be for a single feature/bugfix/chore. All non-trivial changes should be integrated via a pull request for ease of traceability (using short-lived branches). - Everything should be [[Infrastructure-as-Code]]. Never use a browser management console to create/update infrastructure resources without making the equivalent update to a configuration file in your repository. - Do not obsess on getting everything working locally, especially when building a cloud-based system. Isolation and speed are important aspects of an individual developer's environment, but this doesn't always equate to "local". (see [[A fully local development workflow is impossible for most serverless applications]]) - Corollary for this is that every developer building a cloud-hosted system should have their own isolated development resources (databases, queues, API gateways, etc) in the cloud. Ideally each developer has their own AWS account, but at the very least should have their own CloudFormation stacks. - Developers should be given read access to their cloud account's billing for all environments (including production). They are making technical decisions that affect this bill so should be aware of the existing costs. ## Quality & testing - Standardise on lint and formatting rules for your team and make them strict. Automated static analysis is a quick, cross-cutting quality win for your team. Pick a [commonly used public set of default rules](https://www.npmjs.com/package/eslint-config-airbnb) and minimise your team's customisation of these to avoid bikeshedding. - Development and test environments should aim to be as close as possible to production while still maintaining productivity ([[Production-fidelity testing]]). - Every feature you build for a serverless backend should have at least one integration or E2E test (preferably several), as this is where the failure risks are. Unit tests are nice-to-have for each feature, but optional. Writing tests before the code is something I do, but not something I think is really important for others to follow. - Make it easy for developers to add new tests of each category. Ensure you have hooks (e.g. npm script commands and folder structure) in place for running lint/unit/integration/E2E tests when starting a new project. - Use mocking very sparingly when writing tests. Mocks are a deviation from how your code/system will be exercised in production, they require time to setup/inject correctly and risk baking in bad assumptions. The best usage for mocks is: [[Test doubles enable the testing of non-deterministic behaviours of remote services]] - Don't rely on pre-seeded test data from your tests. Instead, have each test create (and teardown) the data it needs to perform its checks. - Encourage all developers on the team to write good docs (see [[Writing technical documentation]]). ## DevOps - Prefer a simple release-from-mainline [[Trunk-based development]] branching model over per-environment release branches. This ensures a single commit can flow through each stage in the release pipeline and you don't need to maintain multiple pipelines for each environment. - Use [[Feature toggle]] to allow incomplete features to be merged to the main branch. - On greenfield projects that are not yet in production usage, encourage a fully automated [[Continuous Deployment]] release model right from the off. This gets stakeholders comfortable with seeing changes frequently released (and with seeing bugs) and also encourages higher test quality/coverage within the engineering team. - Logs are the single most important operational tool at your disposal. Ensure your application code logs consistently at different levels (debug/info/warn/error), using a JSON format to allow for search parsing. ## Career - Always be open to changing your mind. When you do, make a point of telling another person if you've learnt something from them which has caused you to change your mind. Similarly, close the virtuous loop by showing junior engineers that you changed your mind, encouraging them to do so in the future. - Don't make specific technologies or languages a part of who you are as a person. I used to do this and it held me back a lot from understanding alternatives. - Be curious about the organisation you're working in, not just the technologies you're working with. Understand the value of the system you're building and thus the overall value of your contribution towards it. Get to know individuals within the different stakeholder roles involved in designing, operating, using, selling, marketing and paying for the system you're working on. - If someone tells you to do something because it's "best practice", ask why. Context is always key and always changing, and "best practice" doesn't always hold up to many contexts. (see [Beware best practice](https://serverlessfirst.com/beware-best-practice/)) - After a certain level of coding skill, writing better English (documentation, blog posts, emails) is more important for your career prospects than writing better code. This is especially true in an increasingly async, remote-working world.