Key Takeaways
- Shift-left testing is a strategy that changes the development process so that defects are discovered earlier.
- The earlier a defect is detected, the lower its typical cost to fix.
- Detecting defects earlier requires running whatever tests are feasible at each stage as frequently as possible.
- ODC analysis lets you inspect and improve your shift-left testing efforts.
Why Shift-Left Testing Matters
Shift-left testing is a strategy that moves the activities for finding defects—including testing—into earlier phases of the development process. Doing so is said to reduce the cost of fixing those defects.
It is called shift-left because timelines are usually drawn left-to-right; moving testing earlier means moving it leftward.
Shift-left testing assumes the empirical rule of thumb that the sooner a defect is found, the cheaper it is to fix. Numerous sources cite that rule, but the evidence is mixed: some publications support it 12, while others report that the effect appears in some projects but not in others 3.
Even though the rule is not rock-solid, the author believes—based on experience and the four reasons below—that the trend is real.
- The shorter the time between defect injection and detection, the fewer unrelated changes are made in the meantime. The search space is smaller, so the fix costs less.
- When a defect is found shortly after it is introduced, the code has usually not yet been handed to other developers or testers; the original author can fix it alone, avoiding communication overhead.
- If a faulty component is used by other components before it is repaired, those users must also be fixed later. Catching the defect before any downstream code is written avoids cascading rework.
- Early detection shortens the overall project timeline. Figures 1 and 2 show Gantt charts for processes that discover defects at different times.
Figure 1: Gantt chart of a process where developers do not test and rely on dedicated testers
Figure 2: Gantt chart of a process where developers test their own work
In Figure 2, developers can fix defects while development proceeds in parallel, shortening the idle time that blocks others in Figure 1. Viewed globally, the lead time from project start to release is shorter, and shorter lead times mean lower costs.
Throughout this series we will proceed on the premise that finding defects earlier tends to reduce the cost of fixing them.
How to Achieve Shift-Left Testing
To shift left, move verification activities as far upstream as possible so that defects surface sooner. That, in turn, means running all feasible checks at every stage.
Table 1 summarizes typical verification techniques and the kinds of defects they can reveal (some are not “tests” in the narrow sense).
Table 1: Examples of defects each stage can uncover
Verification technique | Representative defects it can detect |
---|---|
Syntax highlighting | Syntax errors |
Static analysis (simple lint checks) | Variable mix-ups, incorrect API usage, etc. |
Static checking (type checking) | Type mismatches, wrong argument lists, etc. |
Unit testing | Violations between component spec and implementation, e.g., wrong branch conditions |
Integration testing | Spec mismatches between integrated components |
E2E / system testing | Spec mismatches across the whole system |
Tester-driven verification | Implementer misinterpretations of the spec |
Failure monitoring | Defects missed by tests, spec flaws in unusual environments |
Customer reports | Defects missed by monitoring, usability problems, etc. |
Crucially, no single technique finds every defect and does so immediately.
Earlier stages detect problems sooner but miss some kinds of issues; later stages catch more kinds of issues but only after a longer delay. You therefore need a blended strategy: catch what you can early, and rely on later stages for the rest. The difference becomes clearer in a simulation.
Let’s Simulate
Figures 3–6 arrange common verification stages from earlier (top) to later (bottom). For a given defect, draw an arrow downward; the arrow stops at the stage where the defect is caught (Figure 3).
Figure 3: How to read the simulation diagrams (1)
If we have two defects caught at different stages (Figure 4), the arrow that stops higher up represents a defect found earlier—and therefore cheaper to fix—than the arrow that stops lower down.
Figure 4: How to read the simulation diagrams (2)
Now compare two development styles. One uses a language without static type checking, edits code in a plain text editor, and developers do no self-testing (Figure 5). The other uses a statically typed language, an IDE-like environment, and developers test their own code (Figure 6).
Figure 5: Process with plain text editing and no developer-run checks
Figure 6: Process with an IDE-like environment and developer-run checks
The latter clearly finds defects earlier, hence at lower cost. That illustrates why running every feasible test as soon as it becomes feasible is worthwhile.
Note that we have discussed only a subset of defect-detection activities. Defects can also be introduced during requirements analysis, specification, and design; shift-left should target those phases too. 4
For example, during specification you can build a working throwaway model (a prototype) and exercise it in limited scenarios to uncover spec defects early. Paper sketches, design-tool prototypes (Figma 5, Sketch 6, Zeplin 7, etc.), even block toys plus a camera for 3-D game terrain—any medium is fine as long as it answers the questions you have.
Writing the spec in a rigorously defined language (formal specification) also exposes unintended ambiguity early, as explained in Part 2. Taken together, these early activities enable shift-left testing.
But teams often leave shift-left opportunities undiscovered. How can you spot them? One answer is ODC analysis.
ODC Analysis
ODC analysis (Orthogonal Defect Classification) classifies every detected defect along four independent attributes: type, trigger, source, and impact (Table 2).
Table 2: The four ODC attributes
Attribute | Meaning |
---|---|
Type | Root cause category—e.g., wrong or missing configuration, incorrect comparison, etc. |
Trigger | Process that revealed the defect—code review, unit test, E2E test, and so on |
Source | History of the faulty component—new, reused, modified, third-party, etc. |
Impact | Kind of failure effect—usability, performance, reliability, deployability, etc. |
Suppose an E2E test of a web app finds that a page fails to open when the user name is null
, due to missing SQL escaping. The type is “missing functionality,” and the trigger is “E2E test.” Recording that information for every defect enables multifaceted analysis.
For shift-left purposes we focus on type and trigger. From type we infer the stage where the defect should have been caught; from trigger we see where it was caught. If actual detection lags behind expectation, we revise the process. After running with the revised process for a while, we perform ODC analysis again to verify that detection has indeed shifted left. Thus ODC provides both insight for improvement and a way to measure the effects of process changes.
-
Capers Jones, Applied Software Measurement: Global Analysis of Productivity and Quality, McGraw-Hill, April 2008. ISBN 978-0-07-150244-3 ↩
-
Karl Wiegers & Joy Beatty, Software Requirements, 3rd ed., Microsoft Press, 2013. ISBN 978-0-7356-7966-5 ↩
-
Tim Menzies, William Nichols, Forrest Shull & Lucas Layman, “Are delayed issues harder to resolve? Revisiting cost-to-fix of defects throughout the lifecycle,” Empirical Software Engineering 22 (4), 2017, 1903–1935. https://doi.org/10.1007/s10664-016-9469-x ↩
-
In Parts 1 (Oct 2024 issue) and 2 (Nov 2024 issue) we defined a defect narrowly as “an input that makes implementation and specification disagree.” Here we use a broader definition: “an input that violates correctness or validity,” so that requirement and specification problems also count as defects. ↩
Top comments (0)