Your team deploys a CDK app to dev and prod accounts from a CI job that runs `cdk deploy`. What would moving to a self-mutating CDK Pipeline (the `pipelines.CodePipeline` construct) change, and when would you keep the plain CI job?
answer
- pipeline defined in the same CDK app
- it redeploys itself before deploying you
- one assembly promoted through stages
- targets bootstrapped with a trust relationship
- merge rights become pipeline control
basics
~20 sA CDK Pipeline defines the delivery pipeline in CDK itself and updates itself: each run synthesizes, redeploys the pipeline stack from that assembly, then runs the application stages. You gain pipeline-as-code across accounts; you take on AWS-native lock-in, a privileged self-modifying pipeline, and slow feedback.
solid answer
~60 sWith a plain CI job, the pipeline lives in your CI system's config and the CDK app is just something it invokes. With `pipelines.CodePipeline`, the pipeline is itself a CDK construct in the same repository: you deploy it once by hand, and from then on every run checks out source, runs the synth step, then a **self-mutation** step that deploys the pipeline stack from the freshly synthesized cloud assembly — so adding a stage, an account or a region becomes a pull request rather than a console change — before running the application stages against that same assembly. The prerequisite is that each target environment is bootstrapped with `--trust` on the pipeline's account. The costs are real: it ties you to CodePipeline and CodeBuild, a pipeline that can rewrite itself and its own permissions is effectively privileged, and you cannot test a pipeline change without merging and waiting for a full run. Keep the plain job when your organization already standardises on GitHub Actions or GitLab with OIDC, or deploys more than CDK.
go deeper
Know that the CDK can define the delivery pipeline itself, not just the application infrastructure, and that such a pipeline updates itself from the repository on each run.
Describe the run order — source, synth, self-mutate the pipeline stack, then the application stages — and that every stage deploys the one assembly produced by that run's synth step.
Bring the operational prerequisites and failure modes: bootstrap trust between accounts, no local testing of pipeline changes, and what it takes to recover when a self-mutation breaks the pipeline.
Own the decision. Weigh pipeline-as-code and multi-account promotion against a self-modifying privileged pipeline, CI fragmentation and slow feedback, and be able to say what makes a plain OIDC-based CI job the better answer for your organization.
## What the plain CI job already gives you A workflow that assumes a role via OIDC, runs `cdk synth`, then `cdk deploy --require-approval never` per environment is a perfectly respectable delivery pipeline. It has no static credentials, it reuses the CI system the rest of the company knows, and its logs and approvals live where developers already look. Its weakness is that the *pipeline itself* is described in a different language, in a different place, with a different review culture than the infrastructure it deploys — and changing it does not automatically follow when a new environment appears. ## What CDK Pipelines changes The `pipelines` module in aws-cdk-lib lets you declare the delivery pipeline as constructs. A minimal shape is a `CodePipeline` with a source, a synth step, and one or more stages: ```typescript const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { synth: new pipelines.ShellStep('Synth', { input: pipelines.CodePipelineSource.connection('org/repo', 'main', { connectionArn: props.connectionArn, }), commands: ['npm ci', 'npm run build', 'npx cdk synth'], }), }); pipeline.addStage(new AppStage(this, 'Prod', { env: prodEnv })); ``` The distinctive behaviour is **self-mutation**. A human deploys the pipeline stack once. After that, each run pulls source, runs the synth step to produce a cloud assembly, executes a step that deploys the *pipeline stack itself* from that assembly, and only then proceeds through the application stages. If your commit added a stage, the pipeline has already grown that stage by the time the stages run, in the same execution. Adding a region, an account or a manual approval gate becomes a reviewed code change like any other. Self-mutation can be turned off with `selfMutation: false`, at which point you are back to updating the pipeline out of band. A second property matters as much: the assembly synthesized once at the start is the artifact every stage deploys. Dev and prod get bytes that were built together, which closes the gap where a re-synth between environments quietly produces something different. ## The prerequisite people trip on Cross-account deployment does not come for free. Each target environment must be bootstrapped with `--trust <pipeline-account>` and an explicit `--cloudformation-execution-policies`, so the pipeline's roles may assume that environment's deploy and publishing roles. That bootstrap is a privileged, human, out-of-band action — which is appropriate, because it is the moment an account agrees to be deployed into by someone else. Teams that automate everything else still tend to keep this manual and audited. ## The tradeoffs to own **Privilege.** A pipeline that can redeploy itself can also change its own permissions, and it does so from whatever is on the tracked branch. That makes merge rights on that branch equivalent to control of the pipeline and, transitively, of what it can do in every trusted account. Branch protection, required reviews and a narrow `--cloudformation-execution-policies` stop being hygiene and become the actual security control. **Feedback latency.** You cannot run the pipeline locally. Testing a change to it means merging and waiting for a full execution, sometimes to discover a typo in a shell step. Compared with a CI config you can iterate on in minutes, this is a genuine productivity cost, felt most while the pipeline is young and changing often. **Lock-in and blast radius.** It is CodePipeline and CodeBuild, AWS-only. If the organization runs one CI system across many languages and clouds, introducing a second one for the infrastructure repo fragments tooling, notifications and on-call familiarity. And a bad self-mutation that breaks the pipeline requires a human with bootstrap-level access to deploy a fix by hand. ## When each answer is right CDK Pipelines earns its place when the estate is AWS-native and multi-account, when environments are added and removed over time, and when the pipeline definition genuinely benefits from living beside the stacks it deploys and being versioned with them. It is strongest for platform teams shipping the same app shape into many accounts. The plain CI job stays right when your organization already has a standard CI system with OIDC federation to AWS, when the repository deploys more than CDK, when contributors are not all AWS-fluent, or when fast iteration on the pipeline itself matters more than describing it in TypeScript. A common middle ground keeps the existing CI system, synthesizes once, publishes `cdk.out` as an artifact, and deploys that same assembly per environment with `cdk deploy --app cdk.out` — capturing the build-once property without adopting a second CI stack or a self-modifying pipeline.
- What must be true of a production account before a CDK Pipeline in a separate tooling account can deploy into it?It has to be bootstrapped with `--trust <tooling-account>` and an explicit `--cloudformation-execution-policies`, so the pipeline's roles can assume production's deploy and publishing roles. Without it the pipeline synthesizes and self-mutates happily, then fails at asset publishing with an assume-role error. That bootstrap is deliberately a manual, privileged action — the account consenting to be deployed into.
- Why does self-mutation change how you think about branch protection?Because a merge to the tracked branch can rewrite the pipeline and the permissions it runs with, not just the application. Anyone who can merge can, in principle, grant the pipeline more access in every trusted account. Required reviews, protected branches and a deliberately scoped CloudFormation execution policy become the real security boundary rather than a nicety.
- How would you get the build-once property without adopting CDK Pipelines?Synthesize once in your existing CI, publish `cdk.out` as a build artifact, and have each environment's job deploy that artifact with `cdk deploy --app cdk.out` after assuming the target account's role via OIDC. Every environment then receives the assembly that was reviewed, which was most of the value, and you keep one CI system and fast iteration on the pipeline definition.
saying these in an interview costs you the question
- Thinks self-mutation rewrites your application source code
- Claims CDK Pipelines works on any CI system
- Assumes cross-account deploys need no bootstrap trust
- Believes a pipeline change can be tested locally before merging
- Scopes the pipeline's permissions to only the application stacks