11 Engineering Principles for Modern Software Development Success
Note: I was going to create 10 principles but I remembered Nigel Tuffnell from Spinal Tap stating that 11 is better than 10.
1. Embrace Continuous Delivery
Optimise your entire workflow by getting small changes safely to production with minimal friction. This principle transforms software delivery from infrequent, high-risk events into a routine, everyday activity.
Engineer: "I approach each feature by breaking it into smaller, deployable increments rather than building everything at once. When implementing our payment API, I first shipped the basic structure with mock responses, then added actual payment processing in subsequent releases – each step was independently valuable and testable."
Tech Lead: "My role shifted from coordinating big releases to removing deployment bottlenecks. Last quarter, we reduced our deployment lead time from days to hours by automating manual approval steps and implementing feature flags that let us deploy code that wasn't yet ready for users."
Architect: "I redesigned our monolithic application into domain-based services that can be deployed independently. This paid off when our checkout service needed urgent updates during a sales event – we deployed those changes without touching our more stable catalogue system."
Tester: "Instead of testing everything at the end of a development cycle, I now verify small changes continuously. I built an automated suite that runs critical checkout flows after every deployment, giving us confidence to release multiple times daily without manual regression testing."
2. Design for Observability
Create systems where internal state and behaviour are easily understood from the outside, allowing teams to answer novel questions about application behaviour without deploying new code.
Engineer: "Rather than adding basic logging, I instrument code to capture the full context of important operations. When implementing our document processing service, I added structured logs capturing not just errors, but processing duration, document size, and customer type – which proved invaluable when diagnosing unexpected timeouts."
Tech Lead: "I established a culture where production access is a normal part of development, not a rare privilege. During a recent outage, our newest team member identified the root cause by directly querying our observability tools rather than waiting for an operations specialist."
Architect: "I design each service to expose consistent health metrics, custom business metrics, and structured logs. This approach helped us identify that our recommendation engine was running efficiently according to technical metrics but producing increasingly irrelevant suggestions according to business metrics."
Tester: "Beyond functional testing, I verify that systems expose the data needed to understand their behaviour. I recently created a test that intentionally triggers error conditions and verifies the logs contain all information support would need to diagnose the issue without developer involvement."
3. Practice Sustainable Engineering
Balance short-term delivery with long-term maintainability by continuously improving code quality and preventing technical debt from accumulating to unmanageable levels.
Engineer: "I follow the 'boy scout rule' of leaving code better than I found it. While fixing a bug in our inventory system, I noticed confusing variable names, so I refactored that section even though it wasn't strictly part of my task – making the next developer's job easier."
Tech Lead: "I allocate about 20% of our capacity to maintenance work and make its value visible to stakeholders. When our legacy authentication system was slowing down feature development, I quantified the impact and secured time for a proper refactoring rather than continued workarounds."
Architect: "I evaluate technologies not just on features but on their long-term sustainability. We chose a less trendy database solution because it had better documentation, a stable API, and an active community – factors that matter more three years into a project than cool features."
Tester: "I actively prune our test suite to keep it valuable and maintainable. Last month, I identified several slow, brittle UI tests that rarely caught issues and replaced them with faster API-level tests that provided similar coverage with less maintenance overhead."
4. Cultivate Engineering Ownership
Create an environment where teams genuinely own their services from conception through production operation, breaking down silos between roles and fostering deeper responsibility for outcomes.
Engineer: "I don't consider features 'done' when they pass QA, but when they're delivering value in production. After launching our new search feature, I monitored error rates and performance, then proactively optimised slow queries before users even complained."
Tech Lead: "I distribute on-call responsibilities across the entire team rather than creating operations specialists. This recently paid off when a production issue occurred, and the engineer who built the feature was able to directly diagnose and fix it instead of trying to explain the system to someone else."
Architect: "I create architecture decision records that explain the 'why' behind decisions but leave teams autonomy on implementation details. For our data pipeline redesign, I established throughput and reliability requirements, then trusted the team to select the right streaming technology."
Tester: "I partner with engineers throughout development rather than being a gatekeeper at the end. When building our checkout flow, I worked alongside developers to create test scenarios that informed the implementation, catching edge cases in design rather than after code was written."
5. Optimise for Feedback
Structure work to maximise learning through rapid experimentation, data collection, and adaptation based on real-world usage rather than speculation.
Engineer: "I build analytics into features before launching them to measure actual usage. For our new document sharing feature, I tracked not just if people used it, but which specific workflows were popular – discovering that 80% of usage was for a scenario we considered secondary."
Tech Lead: "I schedule regular reviews of production metrics after features launch. This practice revealed that our 'recommended products' widget, despite being completed on schedule, was barely receiving any clicks – prompting us to redesign it rather than moving on."
Architect: "I design for incremental evolution based on real-world feedback. Instead of building a comprehensive event-sourcing system upfront, we started with a simpler model and only expanded after validating our assumptions with actual usage patterns."
Tester: "I focus testing efforts on validating assumptions, not just verifying functionality. When testing our new mobile registration flow, I discovered that while it functioned correctly, completing registration took twice as long as competitors – a critical finding that prompted a redesign."
6. Design for Resilience
Build systems that anticipate and gracefully handle failures at all levels, maintaining core functionality even when individual components or dependencies fail.
Engineer: "I write code assuming external services will fail unpredictably. When implementing checkout, I added circuit breakers around the payment processor integration that would temporarily store orders locally if the service was unavailable, then process them when connectivity resumed."
Recommended by LinkedIn
Tech Lead: "I organise regular 'game days' where we purposely break things in production or staging environments. During our last exercise, we killed our primary database and discovered our fallback mechanism had subtle bugs that would have caused data loss in a real outage."
Architect: "I design with failure domains that limit the blast radius of any single issue. When an unexpected failure in our recommendation service recently occurred, it didn't affect critical checkout flows because we'd designed the system to degrade gracefully, showing generic recommendations instead."
Tester: "I create chaos testing scenarios that simulate realistic failures. I built a test that randomly terminates application servers during high load, which revealed that our session management wasn't properly distributed, causing users to lose shopping carts during server failures."
7. Prioritise Developer Experience
Invest in tools, environments, and workflows that maximise developer productivity and satisfaction, recognising that developer experience directly impacts system quality and delivery speed.
Engineer: "I create reusable components and clear documentation for common patterns. After spending hours setting up authentication for a new service, I packaged the solution as a shared library with examples, saving every subsequent project days of implementation time."
Tech Lead: "I measure and optimise the 'time to first commit' for new team members. We recently improved our on-boarding by creating a single script that sets up a complete development environment, reducing setup time from days to hours and eliminating dozens of potential configuration issues."
Architect: "I evaluate technologies based partly on their developer experience impact. We chose a slightly less feature-rich API framework because it had superior documentation, consistent patterns, and better error messages – factors that dramatically reduced the learning curve."
Tester: "I build testing frameworks that make writing tests as friction-less as possible. Our new component testing library allows engineers to test complex UI interactions with just a few lines of code, which increased test coverage because writing tests became easier than arguing against them."
8. Build for Security and Compliance
Integrate security and compliance considerations throughout the entire development life cycle, making secure practices an enabler rather than a bottleneck for delivery.
Engineer: "I treat security as a fundamental requirement, not an add-on. When building our document upload feature, I automatically scanned all files for malware, validated file types, and generated secure temporary URLs – making security the default path rather than an extra step."
Tech Lead: "I include security requirements in our definition of done and build security education into our regular workflow. We now run automated vulnerability scans as part of our CI pipeline, which recently caught an injection vulnerability before code review even happened."
Architect: "I design security controls appropriate to the actual risk level. For our internal reporting tool, we implemented simplified authentication using existing company credentials rather than requiring complex new security measures, balancing protection with usability."
Tester: "I develop penetration testing scenarios based on real-world attack patterns. I created a test suite that attempts common API attacks against our endpoints, which identified that our input validation was rejecting malicious requests but returning error messages that revealed too much about our implementation."
9. Practice User-Centered Engineering
Build features and systems that solve real user problems by deeply understanding user needs and measuring success through user outcomes rather than feature completion.
Engineer: "I ask 'who is this for and what problem does it solve?' before writing a single line of code. When building our reporting feature, I shadowed users first and discovered they needed quick answers to specific questions rather than the comprehensive dashboard we initially planned."
Tech Lead: "I schedule regular user feedback sessions and bring engineers directly into contact with customers. This practice transformed how we prioritised performance work when we learned that users valued consistent response times more than occasional blazing speed."
Architect: "I design systems around user journeys rather than technical domains. By mapping our architecture to key user workflows, we identified bottlenecks that crossed multiple services but significantly impacted the user experience, which weren't visible when looking at individual components."
Tester: "I create test scenarios based on real user behaviours, not just requirements documents. By analysing usage patterns in production, I discovered edge cases in our search functionality that real users frequently encountered but weren't covered in our initial test plans."
10. Embrace Cloud-Native Infrastructure
Treat infrastructure as malleable software rather than fixed hardware, leveraging cloud platforms and automation to create dynamic, scalable, and self-healing systems.
Engineer: "I define infrastructure needs alongside application code, using infrastructure-as-code tools. When implementing our media processing service, I specified not just the application logic but also the exact compute, storage, and scaling requirements as code in the same repository."
Tech Lead: "I approach infrastructure changes with the same rigour as application changes, requiring code review and automated testing. This discipline prevented an outage when a well-intentioned infrastructure change would have removed critical security groups from our production environment."
Architect: "I design for cloud provider portability where it matters while embracing provider-specific services where they add value. For our core services, we use containerisation and Kubernetes for portability, but for specialised needs like ML workloads, we leverage cloud-specific optimised services."
Tester: "I validate infrastructure changes in isolated environments before production. We caught a subtle configuration issue in our auto-scaling rules by running a load test against an exact replica of production, which would have caused cascading failures during our upcoming product launch."
11. Foster Cross-Functional Collaboration
Break down traditional silos between engineering, product, design, and business teams to create shared understanding, aligned goals, and better outcomes.
Engineer: "I participate actively in product discussions, offering technical perspective without shutting down ideas. During a product planning session, I suggested an alternative technical approach that delivered 80% of a complex feature with 20% of the effort, allowing us to test market fit faster."
Tech Lead: "I translate between business requirements and technical constraints, finding creative solutions that respect both. When our marketing team needed a complex promotion engine in an impossible time frame, I helped design a simpler solution that met immediate needs while laying groundwork for the full vision."
Architect: "I build relationships with stakeholders across the organisation to understand broader business context. These connections helped me design our new API platform to support both our immediate product needs and emerging business partnerships that weren't yet on the engineering road map."
Tester: "I align testing strategies with business priorities and risk tolerance. By understanding the business impact of different features, I concentrated our most rigorous testing on our payment flows where bugs would directly impact revenue, while using lighter testing for experimental features."