Summary Notes from the book, Software Engineering at Google
- Style Guides and Rules
- Auto formatting : Resharper console app.
- Code Review
- Review in 3 aspects
- The code will work as intended without error.
- If code looks OK to the code owner’s view.
- If readability is OK
- It might be good to see how many code reviews are made by a developer as it’s unknown effort.
- Idea: static analysis -> when build in CI tool, check all warnings and send them to the committer.
- Tip: make it small.
- Review in 3 aspects
- Documentation
- Wiki type document is hard to get it up to date, and prevent duplicate.
- Solution: add the document in source control together with source. Document should have an owner.
- Reference Document
- Public class/method: must write a comment describing it
- Design Doc: write and review before implementing a major project
- Testing
- Automated testing provide developers change code with confidence.
- Adopting automated testing in Google was a cultural change. e.g. orientation class, test certified program, testing on the toilet. -> demonstrating success rather than mandating it.
- Unit Testing
- Best Practice not to be brittle, but maintainable.
- Test only public API
- Test state rather than interaction
- Clear Test
- Make it clear and concise – be clearer over DRY principle.
- Test behaviours, not methods
- Best Practice not to be brittle, but maintainable.
- Test Doubles
- Realistic tests rather than mocking framework whenever possible: When real logic change, logic in mocking needs to change as well. Consider factors as below.
- Execution time
- Determinism
- Dependency construction
- Faking : it can be better off than mocking. e.g. fake DB, fake service
- Stubbing: stubbing can be a reasonable technique to use, as long as its usage is constrained so that tests don’t become overly complex.
- Prefer state testing over interaction testing
- Realistic tests rather than mocking framework whenever possible: When real logic change, logic in mocking needs to change as well. Consider factors as below.
- Larger Testing
- Larger test is used to cover realistic test that involves multiple dependencies, and systems.
- This is needed because Unit Test only cover function level fidelity.
- Even for integration test, smaller test is better.
- Types of Larger Tests
- Functional testing of one or more binaries
- Browser and device testing
- Performance, load, and stress testing
- Deployment configuration testing
- Exploratory testing : See if system can resilient on unexpected user input, etc.
- A/B diff (regression) testing
- User acceptance testing (UAT)
- Probers and canary analysis (in production): Probers is a simple automated smoke test.
- Disaster recovery and chaos engineering (in production): Can do this in the case where system can affordable.
- User evaluation (in production)
- Deprecation
- When the obslete code, depenencies, system became a burden to manage, deprecate it.
- Tip: announce it in advance, change the obslete code interface name, stop the old system to see who/what is dependent on it. Provide benefit to move to the new system
- Consider the end of product when start builing it. e.g. structure it for a graceful deprecation.
- Version Control and Branch Management
- Do not use Dev branch. Trunk(master) masted, heavily tested and CI, disable incomplete/untested features at runtime.
- Release branch.
- One-version rule: developers must not have a choice where to commit, or which version of an existing component to depend upon.
- Code Search
- Build
- Remote Cashing: At Google, many artifacts are served from a cache rather than built from scratch. The cach is shared by many developers.
- Artifact based building: each build worker generates artifacts independently and shared through the remote cash. A build worker may depend on the artifacts from the other worker. The dependency graph can be managed in artifact based built, but near impossible in task-based build.
- One version rule: the same dependency should be a single version in the system/company.
- For a small projects, task based build system will be enough given the overhead of artifact based build system.
- One-time cost v.s. ongoing problem.
- Automatically managed dependencies can be convenient for small projects, but they’re usually a recipe for Disaster
- we enforce a strict One-Version Rule for all third-party dependencies
- limiting engineers’ power and flexibility can improve their productivity.
- My conclusion: good build system increase developers’ productivity.
- Code Review
- limiting engineers’ power and flexibility can improve their productivity.
- In emergency cases, the author can forcefully commit their change and have it reviewed after commit.
- Critique also surfaces the results from builds, tests, and static analyzers, including style checks
- Diff features
- Syntax highlighting
- Cross-references
- Intraline diffing
- Ignore whitespace
- Move detection
- Code reiveiw request hook can trigger automated testing, etc and let author know if not not successful
- Google greatly values the educational aspects of code review, even though they are more difficult to quantify.
- Static Analysis
- we generally focus on newly introduced warnings, unless existing warnings are critical.
- Key lessions
- Focus on developer happiness
- Effective false positive (perceived false positive) is important.
- Automated fix. e.g. style fix should be an automated fix
- Show error or don’t show warning as developers ignore warnings.
- Make static analysis a part of the core developer workflow
- It’s effective to run static code alanysis when PR raised and report back to author/share with reviwer
- Empower users to contribute
- Set a feedback loop so that developers provide feedback e.g. Not Useful button.
- Focus on developer happiness
- Dependency Management
- Dependency conflict is main concern. i.e. so called diamond dependency. Lib-user may depends on lib-a and lib-b. Both lib-a and lib-b can depend on different version of lib-base each. Lib-user try resolve this by skip forward or backward in version to find compatible version.
- “I got it to work” V.S. “this is working in a supported fashion”.
- Solutions
- Nothing changes – do not change dependency as far as you can.
- Semantic Versioning – major.minor.patch versioning.
- Bundled Distribution Models –
- Live at Head – Version Control & CI make sure all work
- Minimum version selection – select the lowest version possible
- Large Scale Changes
- Large scale changes prone to create a bug or not successful release
- Solution? Create automated tests to cover as much as possible so a large scale changes are covered and rest assured.