Software engineering at [[Google]]
# Software Engineering Definition
>[!quote] #adage
>[[Software engineering is programming integrated over time]].
# Core Concepts
- readability
# Per-chapter
## Ch7. Measuring Engineering Productivity
- [[High Output Management]]-esque argument.
- what *can* we measure, but what *should* we measure?
- metrics are expensive to track;
> [!note] #framework on metric-driven development
> - what result are you expecting, and why?
> - If the data supports your expected result, what action will be taken?
> - If we get a negative result, will appropriate action be taken?
> - Who is going to decide to take action on the result, and when would they do it?
>
> Some refutals:
> - you can't afford to change the process/tools right now
> - any results will soon be invalidated by other factors
> - the results will be used only as vanity metrics to support something you were going to do anyway
> - the only metrics available are not precise enough to measure the problem and ca be confounded by other factors
> [!quote]
> When you are successful at measuring your software process, you aren't setting out to prove a hypothesis correct or incorrect; *success means giving a stakeholder the data they need to make a decision*.
### Goal/Signals/Metrics (GSM) #framework
- goal - desired end result
- signal - how you might know that you've achieved the end result
- metric - proxy for a signal.
---
Need to balance velocity and quality - "I can make your review velocity very fast; just remove code reviews entirely."
> [!note] QUANTS, a #framework for developer productivity signals
> - Quality of the code
> - Attention from engineers
> - Intellectual Complexity
> - Tempo and Velocity
> - Satisfaction
- what result are you expecting, and why?
## Ch11. Testing Overview
> [!note] Chapter 11-14 covers [[Testing]]
Categories of tests - *small, medium, large* instead of *unit-integration*.
- **small** - no sleep, IO, or make blocking calls. (File system calls for fixtures are exempt).
- **medium** - can span multiple processes, threads, and make blocking calls including network. however, they can only call `localhost` (contained within a single machine). WebDriver tests fall under this.
- **large** - remove the `localhost` restriction. can run against a remote cluster. reserved for full-system end-to-end tests, legacy components that make impossible to use *test doubles*.
![[Testing Pyramid]]
*Testing Pyramid vs Testing Ice Cream*
## Ch13. Test Doubles
> [!thought]
> This section captures quite a lot what I thought about mocks; if you're mocking everything out, then what are you *actually* testing? I'm glad that I'm not gaslit to believe that we should always shun "real implementation".
> [!note] A *Test Double*
> is an object or function that can stand in for a real implementation in a test.
#definition
- **test double** - covers all instances of the alternative implementations.
- **mock** - created by mocking frameworks (mockito). Very easy to create an ad-hoc stub or fake, but their behavior isn't very real.
- **stub** - a test double with simple, hard-coded behaviors. Stubs are simle, but they can create unclear, brittle, and less effective tests, especially if they are overused.
- **fake** - a test double which provides a realistic implementation (ex: fake, in-memory filesystem). Is normally high quality.
- **seams** - integration points within code that allows test doubles to be introduced. [[Dependency Injection]] is the most common pattern.
The chapter is generally against mocking, with the mantra *prefer realism over isolation*;
> [!quote]
> At Google, the preference for real implementations developed over time as we saw that overuse of mocking frameworks had a tendency to pollute tests with repetitive code that got out of sync with the real implementation and made refactoring difficult.
> ...
> Preferring the real implementation in tests is known as *classical testing*.
> [!quote]
> We prefer realistic tests because they give more confidence that the system under test is working properly. If unit tests rely too much on test doubles, an engineer might need to run integration tests or manually verify that their feature is working as expected in order to gain this same level of confidence.
> ...
> Using real implementation can cause your test to fail if there is a bug in the real implementation. This is good! You *wnat* your tests to fail in such cases because it indicates that your code won't work properly in production.
The example of `@DoNotMock` annotation.
### On Fakes
Fakes provide high-fidelity real-like implementation. (ex: `FakeFileSystem`).
Fakes require domain expertise to implement, and they need to be continuously maintained. They should be *tested* to ensure that fakes resemble the real implementation.
### State vs Interaction Tests
On testing state (either returned or final) vs interaction testing (ex: is method X invoked?)
> [!quote] #adage prefer state testing over interaction testing
## Ch14. Larger Testing
Why?
- unfaithful doubles
- configruation issues
- issues that arise under oad
- unanticipated behaviors, inputs, and side effects
- emergent behaviors and the "vacuum effect"
Why not?
- Reliable
- Fast
- Scalable
- Question of *ownership*
- Lack of standardization
SUT - *Sytstem Under Test*