When "Shift Left" Isn't Quite Right
- Jeremy Druin

- 17 hours ago
- 10 min read
I recently had a discussion about secrets management at a CISO forum that turned into a much broader conversation about DevOps security. We spend a lot of time thinking about whether a security control can detect something, but considerably less time thinking about when in the engineering process that detection occurs. In a GitOps or DevOps workflow, timing can be at least as important as the detection itself.
I use secrets management throughout this article because it provides a clear example of how the timing and placement of a security control can change the outcome. The same principles apply much more broadly across DevSecOps, including vulnerability management, SAST, DAST, software composition analysis, pipeline security, infrastructure-as-code scanning, and other security activities integrated into the software delivery lifecycle. The specific control may change, but the architectural questions remain the same:
Where should detection occur?
Where should enforcement occur?
How should information reach the developer?
How much human effort should the process require?
Detect Early, but Enforce Where It Matters
"Shift left" was once an overwhelmingly useful aspiration. Security activities were routinely performed far too late in the development lifecycle, sometimes after software was already built or deployed. In that environment, almost everything needed to move left.
As DevSecOps has matured, however, the bumper-sticker version of "shift left" has become overly simplistic. The objective is not to move every security control as far left as technically possible. Different points in the development lifecycle provide different levels of reliability, context, and opportunity to prevent an undesirable outcome.
Prevention is ideal, but reliable detection is mandatory. An early control that developers can bypass, that may not be installed, or that may simply fail cannot replace a later control at a choke point we know every change must cross. We should absolutely detect and prevent problems earlier when we can, but we also have to design for the possibility that the earlier controls miss something.
Consider a secret accidentally committed to source code. Discovering that secret after it has been pushed to GitHub is certainly valuable, but by then the problem already exists. The secret may be in the repository history, other developers may have cloned it, and automated processes may have consumed it. Simply deleting the secret from the current version of the file does not undo any of that. The credential may need to be revoked and replaced, and there may be work required to determine where else it propagated.
Finding the same secret before it enters the repository is a very different security outcome. Instead of responding to a credential exposure, we prevented one. That sounds like a straightforward argument for shifting security as far left as possible, but I don't think that is quite right either. Moving a control earlier only improves security if we can depend on the control being there and working.
Ideally, I would like the developer's IDE to identify a secret while the developer is writing the code. A pre-commit check is another excellent opportunity. Both provide fast feedback, and both give the developer an opportunity to correct the problem before it progresses any further. The difficulty is that these controls live on developer workstations. They depend on plugins being installed, configured, updated, and working consistently across every development environment. They may also depend on the developer responding appropriately to the warning.
That makes IDE and pre-commit detection valuable, but I would not make either the primary enforcement point.
The code repository gives us something much more useful architecturally: a choke point aka a "gate". If code must cross that boundary to enter the organization's repository, secret scanning can be made part of that process. A push containing a secret should be rejected before the secret becomes part of the organization's repository and its history. IDE and pre-commit scanning can then make that experience faster and more convenient, while repository enforcement makes it dependable. This distinction matters beyond secrets management.
There is a difference between the earliest place where we can detect a security problem and the earliest place where we can reliably prevent the problem from progressing.
Good DevSecOps architecture should use both but prevention is the priority.
Meet the Developer Where They Work
The next question is what happens when the scanner finds something.
If a developer attempts to commit code containing a secret, the developer is already in the middle of another task. The developer's objective is to get the code committed. Sending the finding to a security dashboard, creating a ticket, emailing a report, or waiting for a security analyst to contact the developer forces the developer to leave that workflow, reconstruct the context somewhere else, determine what security wants, fix the problem, and then return to the original task.
We should instead return the finding through the workflow the developer is already using. Tell them where the secret was detected and what they need to change so they can successfully commit the code. If the approved remediation is to move the value into the organization's secrets manager and reference it from the application, say exactly that.
Security Products, Please Help Me With This
This is also not the right moment to explain the history of credential theft, teach a lesson about why secrets are dangerous, or present every piece of information the scanner knows about the finding. There is a place for training and proactive secure-development guidance, but it is not while someone is trying to resolve a blocked commit.
Cognitive Load Theory gives us a useful explanation for why. John Sweller's research demonstrated the limitations of working memory and what happens as cognitive demands increase. A developer troubleshooting a blocked commit is already carrying the application, code, error, intended change, and development environment in working memory. Sending that person into another system and surrounding the immediate problem with information that is not necessary to solve it consumes more of a limited resource.
The security finding should immediately answer the only question the developer actually has at that moment:
What do I need to do next to meet my objective?
Automate the Remediation, Not Just the Detection
There is one more question we should ask before telling the developer what to do next:
Does the developer actually need to do it?
We have become very good at automating security detection while leaving much of the remediation process manual. A scanner identifies a problem automatically, and then a human is expected to interpret the finding, determine the appropriate change, make the change, and submit it for review.
Where remediation can be performed safely and deterministically, we should automate as much of that process as possible. If a dependency can be upgraded safely, propose the upgrade. If an infrastructure-as-code configuration can be corrected with sufficient confidence, prepare the change. If a security control can generate the appropriate remediation, the developer should not have to manually translate a security finding into code simply because that is how the process has traditionally worked.
That does not mean security tooling should silently rewrite production applications. Automation and autonomy are not the same thing. The existing engineering process already gives us mechanisms for reviewing changes. In many cases, the ideal remediation may be for the system to prepare the change and create a pull request. The developer's involvement can then be reduced to reviewing and approving the proposed change while the existing pipeline performs its normal validation.
Not every finding can be handled this way. Some vulnerabilities require application context and engineering judgment. A leaked credential may require rotation, investigation, or architectural changes that cannot be safely inferred by a scanner. Human judgment remains valuable precisely where judgment is actually required.
The objective is not to remove the developer from security. It is to avoid consuming developer attention for work the system could have performed before asking for that attention. I often tell development communities that I want them to have security, but I do not necessarily want them to have to do security.
Before asking someone how to fix a security problem, ask how much of the fix the system can safely do for them.
Work With the Developer's Objective
There is a similar lesson in organizational incentive research. Agency theory examines what happens when the objectives and incentives of an agent do not perfectly align with those of the principal. In this case, the developer has been asked to produce working software. We should not be surprised that getting the code committed is more immediate to the developer than remembering to perform a separate security activity.
A security process that depends on every developer remembering to manually invoke a scanner is consequently a weak control. It requires an action that is separate from the developer's primary objective and then depends on that action occurring consistently across the organization. The better design is to make scanning native and automatic. The developer attempts to perform the normal development activity, the security control runs because that activity occurred, and intervention happens only when the developer needs to take action.
Don't make security another task the developer has to remember. Make it part of the task they are already performing.
Be Careful What You Buy
This same principle should influence how DevSecOps products are evaluated. Security products are understandably designed to impress security teams. Demonstrations tend to emphasize detection capabilities, dashboards, reporting, policy configuration, integrations, and the amount of information the product can provide about a finding. Those capabilities matter, but the person evaluating the security console and the person who has to live with the product every day are often not the same person.
The developer experiences the tool differently. When something is detected, does the result appear naturally in the IDE, terminal, pull request, or pipeline where the developer is already working? Or does the developer have to open another portal, locate the finding, interpret security terminology, correlate it back to the code, make the change, and then return to the development environment?
That should be part of the product evaluation. Ask the vendor to demonstrate the developer's experience, not just the security team's experience. Show what happens when a developer introduces a secret. Show where the developer learns about it. Show what the developer needs to do next. Show how many systems the developer must visit before being able to continue working.
A security product can have an excellent security dashboard and still create a poor security process.
Evaluate DevSecOps tooling from the developer's chair, not just the security dashboard.
Don't Turn Security Engineers Into Tool Administrators
This also changes how I think about the security tooling itself. Security teams have finite engineering capacity. Every scanner that must be installed, patched, upgraded, monitored, and maintained consumes some of that capacity.
Where possible, I prefer ephemeral scanners that execute in containers and disappear when the task is complete, or mature scanning services that provide the capability without requiring the security team to maintain another persistent software platform. Agents and workstation tooling can still be useful, particularly as additional early detection, but they do not always need to be the foundation of the control.
Operational effort is part of the economics of a security control. The cost of a product is not merely its license. It includes the security engineering time required to operate it.
Security engineering time should be spent engineering security, not maintaining security tools.
Separate Governance From Mechanics
Secrets storage deserves the same architectural scrutiny. There can be good reasons for enterprise-wide secrets platforms, but centralization should not become an objective by itself.
A common argument for centralizing every secret into one vault is visibility. Security wants to know where secrets are located, whether they comply with organizational policy, when they were rotated, who or what can access them, and where exceptions exist. Those are legitimate requirements, but they are governance requirements.
The mechanics answer a different question: How does the workload actually obtain and use the secret?
For the mechanics, I generally want to live off the land as much as practical. If an application is running in Google Cloud, Azure, or AWS, the native secrets and key-management capabilities may provide the most direct integration with the workload, IAM, infrastructure-as-code, Terraform, rotation, and other automation already available on that platform. Forcing a cloud-native workload through an additional secrets infrastructure can sometimes add complexity while preventing us from taking advantage of capabilities the platform already provides.
Governance can still be centralized. Information security should have a common view showing where secrets reside and whether those secrets meet centrally defined policy. But achieving one pane of glass does not require every secret to physically reside in one vault.
This distinction becomes especially important in multi-cloud environments. Standardizing governance does not require standardizing every implementation detail underneath it.
Centralize the policy and visibility. Let the mechanics remain native to the architecture.
The Best Secret May Be No Secret
More importantly, secrets management should not begin with the assumption that every secret needs to be managed better. The first question should be whether the secret needs to exist at all.
Workload identity federation can replace long-lived credentials used by workloads. Workforce identity federation can reduce credentials distributed to people. Managed identities can allow cloud resources to authenticate without embedded application credentials. Certificate-based approaches can replace persistent SSH keys with short-lived credentials. Automated key-management capabilities can remove still more opportunities for people to create, copy, store, and eventually leak secrets.
That is a better outcome than building increasingly sophisticated systems for protecting credentials we could have eliminated.
Before deciding how to protect a secret, ask whether you can eliminate it.
Design With the Economics and Psychology, Not Against Them
The conversation about secrets management therefore becomes much larger than choosing a scanner or a vault. We have to consider the entire lifecycle. Can we eliminate the credential? If we cannot, where should it live? How will the application retrieve it? Where can we detect misuse or accidental exposure? At what point can we reliably prevent that exposure? When something is detected, how quickly can we return an actionable answer to the person who can correct it?
For source code, I want detection as close to the developer as practical because early feedback is cheap. I also want enforcement at the repository boundary because that is where I can reliably prevent the secret from entering Git history and multiplying through clones and automation. When the control fires, I want the developer told immediately, in the workflow they are already using, exactly what they need to change.
I do not want the process to depend on someone remembering to perform a security task, and I do not want the security team spending unnecessary time maintaining infrastructure when an ephemeral or managed capability can perform the same function.
There is an economic and psychological reason for designing the system this way. Developers are incentivized to deliver working software. Security engineers have limited time and engineering capacity. Every additional tool, manual step, context switch, and decision has a cost. Cognitive load is finite, and people predictably optimize around the objectives and incentives of their roles.
Status quo bias gives us another reason not to invent a parallel security workflow. If developers already write, commit, and push code through an established process, security should become part of that process rather than asking them to leave it.
We can fight those forces by asking people to remember more security tasks, visit more dashboards, operate more infrastructure, and interrupt their work more frequently. Or we can design security controls that work with them. Make the secure action part of the developer's existing objective. Automate the security activity when possible. Interrupt someone only when they need to act. Give them only the information necessary to take that action. Preserve scarce security engineering capacity for problems that actually require security engineering.
"Shift left" helped security recognize the cost of finding problems late in the development lifecycle. But simply moving every security activity farther left is not an architecture. The harder and more useful question is deciding exactly where each security activity belongs, taking into account not only the technology, but also the economics, incentives, and human behavior surrounding it.
Good security architecture should work with the economics, incentives, and psychology of the people using it, not require them to overcome those forces.

Comments