# Software Development Lifecycle (SDLC)

> The SDLC is the sequence of phases, from planning to maintenance, that a team uses to build, ship, and change software, with a review gate after each.

Also known as: software development life cycle, software lifecycle, SDLC phases, SDLC models

Source: https://socket.dev/glossary/software-development-lifecycle-sdlc

Last updated: 2026-11-04

The software development lifecycle (SDLC), also written software development life cycle, is the sequence of phases a team uses to take software from idea to retirement: planning, requirements, design, implementation, testing, deployment, and maintenance. Each phase produces an artifact and ends at a gate that decides what moves forward. Those gates decide which code gets reviewed before it runs.

The most important fact about the SDLC today is that most of the code it ships never passes through it the way the model assumes. The SDLC assumes code enters during implementation, written by your team, and gets checked during testing. Third-party dependencies enter at every phase instead, and they often execute before any gate sees them. When we added Express 5.3.0 to an empty project on November 4, 2026, npm installed 65 packages whose maintainer lists include 85 different npm accounts. Our position: an SDLC that only governs first-party code does not govern most of what ships. Every point where a dependency is resolved, installed, or updated is a phase boundary, and each one needs its own gate.

## What is the SDLC?

The SDLC is a model of the work between deciding to build software and switching it off. It names the phases, the artifact each phase hands to the next, and the checkpoint where someone decides the artifact is good enough. Teams adopted it because the cost of a mistake grows with every phase it survives. A wrong requirement caught in planning costs a conversation. The same mistake caught after release costs a rewrite, a migration, and an apology.

The sequential model most people picture comes from a 1970 paper by Winston Royce, ["Managing the Development of Large Software Systems,"](https://faculty.washington.edu/hazeline/misc/reserve/royce_waterfall.pdf) presented at IEEE WESCON in August 1970. Royce drew requirements, analysis, design, coding, testing, and operations as a one-way sequence, then rejected it as drawn. "I believe in this concept, but the implementation described above is risky and invites failure," he wrote. His objection was that testing comes last, so problems surface only after every design decision is already built. The word "waterfall" never appears in the paper. The industry named the diagram he warned against and adopted it anyway.

The formal reference today is [ISO/IEC/IEEE 12207:2017](https://committee.iso.org/sites/jtc1sc7/home/projects/flagship-standards/isoiecieee-122072017.html), the international standard for software life cycle processes. Its [table of contents](https://cdn.standards.iteh.ai/samples/63712/0bc6d9107eeb46d88dbc7b628b12cf4b/ISO-IEC-IEEE-12207-2017.pdf) lists 30 processes in four groups. Two of those 30 are agreement processes: acquisition and supply. The standard treats obtaining software from a supplier as a process of its own, separate from building it. That idea matters later in this article, because nobody runs an acquisition process for `npm install`.

A secure SDLC adds security activities to each phase, such as threat modeling during design and static analysis during implementation. We cover that variant on its own page, the [secure software development lifecycle (SSDLC)](https://socket.dev/glossary/secure-software-development-lifecycle-ssdlc), and the main U.S. reference for it is the [NIST Secure Software Development Framework](https://socket.dev/glossary/nist-ssdf).

## What are the phases of the SDLC?

The SDLC is usually drawn as seven phases, though some sources merge planning with requirements or testing with implementation. The names vary more than the substance. Each phase answers one question and produces one artifact the next phase depends on.

| Phase | Question it answers | Artifact it produces | Typical gate |
| --- | --- | --- | --- |
| Planning | Should we build this, at what cost and by when? | Scope, estimates, project charter | Go or no-go decision |
| Requirements | What must the software do? | Requirements spec or user stories | Stakeholder sign-off |
| Design | How will it be built? | Architecture, interfaces, technology and library choices | Design review |
| Implementation | Write it | Source code and commits | Code review on a pull request |
| Testing | Does it meet the requirements? | Test results and defect reports | Passing test suite, QA sign-off |
| Deployment | Is it running for users? | Release artifact and deployed environment | Release approval |
| Maintenance | Is it still correct, secure, and supported? | Patches, updates, end-of-life plan | The same gates, re-run for each change |

Two things about this table are easy to miss. First, every gate reviews an artifact your team produced: a document, a design, a diff, a test report. Second, the only phase that is supposed to introduce new code is implementation, and the only phase that is supposed to judge code is testing. Both assumptions break as soon as the code was written by someone else.

## SDLC models: waterfall, V-model, iterative, agile, and DevOps

An SDLC model is a rule for ordering and repeating the phases. The phases stay the same. What changes is how many times you go around, how big each pass is, and how fast feedback from testing and operations returns to design.

The models arrived as answers to the problem Royce described. Barry Boehm's [spiral model, published in IEEE Computer in May 1988](https://doi.org/10.1109/2.59), repeats the phases in loops sized by the biggest open risk. The [Agile Manifesto](https://agilemanifesto.org/history.html), written by 17 developers at Snowbird, Utah, on February 11 to 13, 2001, made short cycles with working software the default. DevOps then automated the cycle into a pipeline that runs on every commit.

| Model | How the phases run | When feedback arrives | Best fit | Breaks when |
| --- | --- | --- | --- | --- |
| Waterfall | Once, in sequence | At the end, in testing | Requirements fixed by contract or regulation | Requirements change mid-project |
| V-model | In sequence, each design level paired with a test level | Per level, but still late | Safety-critical systems that need traceability | Same as waterfall, and change is expensive |
| Iterative or spiral | Repeated loops, each sized by the biggest open risk | After each loop or prototype | Large systems built on unproven technology | Loops never close, and the project drifts |
| Agile (Scrum, Kanban) | All phases in short cycles of a month or less | Every cycle, from users | Products whose requirements evolve | No real user feedback, or long-range architecture is ignored |
| DevOps and continuous delivery | Every commit runs through an automated pipeline | Minutes, from tests and production telemetry | Services shipped daily or faster | Gates become automated checks that only see what they test |

Our verdict: use a sequential model (waterfall or V-model) only when requirements are fixed by contract or regulation and a late change is more expensive than slow delivery, as with flight software or medical devices. Use iterative or spiral when the biggest risk is technical and you need prototypes to retire it. For almost everything else, use agile delivery on a DevOps pipeline. The trade is that DevOps compresses the gates into automated checks that run in minutes, which is fast enough to ship daily and too fast for anyone to read what a dependency update actually changed.

That compression is where the SDLC and the software supply chain meet. In a continuous delivery pipeline, design, implementation, build, test, and deploy can all happen within an hour of a commit. Every one of those stages can resolve and run third-party code.

## Where do third-party dependencies enter the SDLC?

Third-party dependencies enter the SDLC at every phase, not only during implementation. A dependency is code your team did not write, chosen by name and version range, fetched from a registry, and executed with your credentials. The figure below marks where that happens in a typical JavaScript project, and where the usual gates sit.

> Chart of SDLC phases. For each phase, a filled dot marks when third-party code is resolved or runs and a hollow diamond marks the gate that reviews work. In implementation, build, testing, and deployment the dot comes before the diamond. In maintenance, versions change with no gate unless an update pull request exists. Planning and requirements bring in no third-party code.

| Phase | How third-party code enters | Gate that reviews the work | Does the code run before the gate? |
| --- | --- | --- | --- |
| Planning | It does not | Go or no-go decision | Not applicable |
| Requirements | It does not | Stakeholder sign-off | Not applicable |
| Design | A library is chosen by name, by a developer or an AI agent | Design review, which sees the name, not the code | No code runs yet, but the choice is made |
| Implementation | npm install resolves and writes the full tree, 65 packages for Express | Code review, which sees one line of package.json | Yes |
| Build (CI) | Fresh runners install from scratch, and npx tools resolve outside the lockfile | CI checks | Yes |
| Testing | Test tools install their own dependencies | Test suite, which checks requirements and not intent | Yes |
| Deployment | Release and signing jobs fetch tools at run time | Release approval, which sees the build and not what it fetched | Yes |
| Maintenance | Version ranges and update bots change resolved versions continuously | None, unless an update pull request exists | Yes, with no gate at all for range drift |

The developer laptop is the earliest and least watched entry point. Running `npm install` resolves the full dependency tree, including every [transitive dependency](https://socket.dev/glossary/transitive-dependency) your direct dependency declares, and writes it to disk before anyone reviews a diff. Until July 2026, npm also ran each package's [install scripts](https://socket.dev/glossary/postinstall-scripts) by default at that moment. [npm v12, released on July 8, 2026](https://socket.dev/blog/npm-12), turned preinstall, install, and postinstall scripts off by default. [pnpm made the same change in version 10.0.0](https://socket.dev/blog/pnpm-10-0-0-blocks-lifecycle-scripts-by-default) in January 2025. Default-on install scripts were a design mistake, and both changes correct it. Older npm releases still run them, and as of November 2026 the Node 22 machine we tested on came with npm 10.9.9.

Here is what one implementation-phase decision pulls in. We ran this on November 4, 2026, with npm 12.2.1 and Node 22:

```bash
$ mkdir app && cd app && npm init -y > /dev/null
$ npm --version
12.2.1
$ npm install express
added 65 packages, and audited 66 packages in 1s

30 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
$ grep -A2 '"dependencies"' package.json
  "dependencies": {
    "express": "^5.3.0"
  }

# Count the npm accounts that can publish any package in the tree
$ jq -r '.packages | to_entries[] | select(.key != "")
    | "\(.key | sub(".*node_modules/"; ""))@\(.value.version)"' package-lock.json \
  | xargs -I{} npm view {} maintainers --json \
  | jq -r '.[] | if type == "string" then split(" ")[0] else .name end' \
  | sort -u | wc -l
85
```

One line in `package.json` produced 65 packages. Counting the `maintainers` field of each resolved version on the npm registry, 85 distinct npm accounts can publish a new version of at least one of them, and 17 distinct publisher identities, one of them GitHub Actions trusted publishing, uploaded the exact versions we got. Every one of those accounts is now part of your implementation phase. None of them appear in your code review.

## Why does the SDLC miss dependency risk?

The SDLC misses dependency risk because its gates review changes to your repository, and dependency resolution changes the code you run without changing your repository. Three assumptions in the model break.

### Code enters through commits

The first broken assumption is that new code arrives as a commit. A [lockfile](https://socket.dev/glossary/lockfile) records an exact tree, but only for the tree that was already resolved. Most dependency declarations are ranges: in the Express tree above, 122 of the 125 dependency edges in `package-lock.json` use a range rather than an exact version. Pinning your own direct dependency exactly does not pin what it depends on. Two installs of the same manifest, two weeks apart, produce different code:

```bash
# Two empty directories, the same package.json, express pinned exactly
$ cat package.json
{"name":"app","version":"1.0.0","dependencies":{"express":"5.3.0"}}

$ npm install --before=2026-10-20T00:00:00Z   # directory a: the registry as of Oct 20
$ npm install                                  # directory b: the registry on Nov 4

# Resolved versions that differ between the two package-lock.json files
body-parser   2.3.0   ->  2.4.0
encodeurl     2.0.0   ->  2.0.1
on-finished   2.4.1   ->  2.4.2
qs            6.16.1  ->  6.17.1
```

Four packages changed with no commit, no pull request, and no review. Nothing here is malicious. The same mechanism is what made [the Axios compromise](https://socket.dev/blog/axios-npm-package-compromised) spread. On March 31, 2026, an attacker published axios@1.14.1 and axios@0.30.4. The only meaningful change was a new dependency, plain-crypto-js@4.2.1, which installed a remote access trojan. Any fresh install that matched a caret range like `^1.14.0` resolved to the malicious version without a change to the consuming project. The timeline below is reconstructed from npm registry timestamps and Socket's reporting.

> Timeline of the Axios compromise in UTC. March 30 05:57, plain-crypto-js 4.2.0 published as a clean copy. March 30 23:59, plain-crypto-js 4.2.1 published with the payload; this is T plus 0. T plus 6 minutes, Socket flags it as malicious. T plus 23 minutes, axios 1.14.1 published. T plus 62 minutes, axios 0.30.4 published. T plus 3 hours 26 minutes, npm replaces plain-crypto-js with a security placeholder. T plus 24 hours, a pnpm 11 default cooldown would first allow the malicious version.

| Time (UTC) | Offset | Event |
| --- | --- | --- |
| Mar 30, 05:57 | T-18h | plain-crypto-js@4.2.0 published, a clean copy of crypto-js with no payload |
| Mar 30, 23:59 | T+0 | plain-crypto-js@4.2.1 published with the remote access trojan installer |
| Mar 31, 00:05 | T+6m | Socket flags plain-crypto-js@4.2.1 as malicious |
| Mar 31, 00:21 | T+23m | axios@1.14.1 published with plain-crypto-js as a dependency; fresh installs matching ^1.14.0 resolve it |
| Mar 31, 01:00 | T+62m | axios@0.30.4 published on the 0.x line |
| Mar 31, 03:25 | T+3h26m | npm replaces plain-crypto-js with a security placeholder |
| Mar 31, 23:59 | T+24h | The first moment a 24-hour cooldown, such as pnpm 11's default, would allow plain-crypto-js@4.2.1; it was already gone |

The exposure did not stop at projects that listed Axios. In [the Axios compromise's hidden blast radius](https://socket.dev/blog/hidden-blast-radius-of-the-axios-compromise), we observed a CI pipeline that ran a CLI through [npx](https://socket.dev/glossary/npx-npm-package-runner) and pulled the malicious dependency through a transitive Axios range, with no project lockfile involved. As that post puts it, "Lockfiles make existing installs deterministic. They do not make new installs safe."

### Testing finds defects

The second broken assumption is that the testing phase catches bad code. Tests verify that software does what its requirements say. A malicious dependency that reads environment variables and posts them to a remote server does not break any requirement, so every test passes. Vulnerability scanning in the testing phase has the same blind spot from a different direction: it matches packages against known CVEs, and malware does not get a CVE. Our Express install above reported "found 0 vulnerabilities," which is true and says nothing about what the code does. Analyzing what is actually in the package, rather than what its metadata and advisories claim, is the only test that applies.

### The supplier is accountable

The third broken assumption is that third-party code went through someone else's equivalent of your SDLC. Every dependency does have its own lifecycle, run by its maintainer, and your SDLC inherits its weakest gate. In the Axios case the weak point was the maintainer's own machine. [The maintainer confirmed a targeted social engineering attack](https://socket.dev/blog/axios-maintainer-confirms-social-engineering-behind-npm-compromise), and said he believes the attacker hijacked his active npm and GitHub sessions. An attacker holding the maintainer's own sessions is not stopped by 2FA or OIDC-based publishing. Provenance and signatures are necessary, but they cannot tell you that a legitimate account was being driven by someone else.

The release phase is exposed the same way. [OpenAI disclosed that the malicious Axios version ran in its macOS app-signing workflow](https://socket.dev/blog/axios-supply-chain-attack-reaches-openai-macos-signing-pipeline-forces-certificate-rotation) on March 31, 2026, in a GitHub Actions job with access to code signing certificates. OpenAI attributed it to a floating tag instead of a commit hash and no minimum release age for new packages. It rotated the certificate and required users to update. The signing step is the last gate in most SDLC diagrams, and third-party code ran inside it.

## Putting gates where dependencies enter

A dependency gate belongs at the moment of resolution or installation, not at the next code review. Each control below covers one entry point and has a cost.

| Entry point | Control | What it costs | What it misses |
| --- | --- | --- | --- |
| npm install on a laptop | Install scripts off by default (npm 12, pnpm 10 and later) | Packages with native builds need explicit approval | Malicious code that runs on import, and scripts you already approved |
| CI build | Committed lockfile plus npm ci or pnpm install --frozen-lockfile | Every update needs a deliberate lockfile change | New dependencies, npx, global installs, and tools outside the project |
| Any fresh resolution | Cooldown: a release-age setting in npm, pnpm, Yarn, or Bun | Security patches wait the same window | Malicious versions older than the window, and anyone can set it to zero |
| Any download from a registry | Package firewall: a proxy that checks each package request | A proxy every installing machine must route through | Malicious packages that have no verdict yet |
| Pull request | Dependency-aware review of manifest and lockfile changes | Review time on every version bump | Anything resolved outside a pull request |
| Maintenance triage | Reachability analysis of known CVEs | Call-graph analysis per language | Malware, which has no CVE to triage |

Two of these involve Socket, so to be specific about what they do. [Socket Firewall](https://docs.socket.dev/docs/socket-firewall-overview) runs as an HTTP and HTTPS proxy between the package manager and the registry and blocks known malicious packages at any depth before they are downloaded. It is free in wrapper mode, as in `sfw npm install`, and it covers installs that never touch a pull request, such as a laptop or an `npx` call in CI. It blocks only what already has a malicious verdict. [Socket for GitHub](https://docs.socket.dev/docs/socket-for-github) watches pull requests that change manifests and lockfiles, analyzes each added package, and comments when it finds signals such as a new install script. It sees only dependency changes that arrive as a pull request.

The cooldown deserves a warning label. A [dependency cooldown](https://socket.dev/glossary/dependency-cooldown) refuses versions younger than a set age. [npm added a minimumReleaseAge setting](https://socket.dev/blog/npm-introduces-minimumreleaseage-and-bulk-oidc-configuration) in its 11.x releases, and [pnpm 11 made 24 hours the default](https://socket.dev/blog/pnpm-11-adds-new-supply-chain-protection-defaults) in April 2026. A 24-hour cooldown would have kept the malicious dependency out, because npm replaced plain-crypto-js with a security placeholder about three and a half hours after the malicious version was published. It also delays every security patch by the same window, and anyone can set it to zero in configuration.

Update bots move the maintenance phase onto a schedule. [Automated dependency updates](https://socket.dev/glossary/automated-dependency-updates) are good for patch latency and dangerous when merged without review, because the pull request diff shows a version number, not the code that changed. A [package firewall](https://socket.dev/glossary/package-firewall) at install time, or a dependency-aware review on those pull requests, is what turns a version bump back into something a human or a tool actually inspected.

## The phase the diagrams leave out

ISO 12207 models acquisition as an agreement process between an acquirer and a supplier. Open source acquisition happens on every install, in every phase, many times a day, and nobody accepts anything. We think the SDLC should draw dependency ingestion as its own recurring phase, owned by someone, with a gate wherever resolution happens.

The open problem is that resolution is leaving the places we know how to gate. AI coding agents now pick packages during design and install them during implementation, often in a sandbox that never produces a commit. As Socket strategic advisor Brad Arkin wrote in June 2026, ["Code now enters in more places than the org chart admits."](https://socket.dev/blog/the-code-you-didnt-write-is-still-yours-to-defend) An SDLC that only reviews what reaches the repository is reviewing a shrinking fraction of the code that runs.
