Below are over 50 examples for inspiring your Architecture Principles. Expand upon them to fit your specific use cases, drive your culture, and remove ambiguity.
Independently Deployable: Modular Software, Modular Teams. Isolate software between teams to increase flexibility and robustness. This approach promotes efficiency, maintainability, and adaptability.
Pit of Success: Make it easy to do the right thing and hard to do the wrong thing. Design intuitively to guide users and developers towards success, making correct paths easy and mistakes difficult.
Contribute to Open Source: Foster community collaboration, promote knowledge sharing, improve software quality, and give back.
Deploy Continuously: Emphasize regular, automated testing and deployment to enhance rapid iteration, resilience, and adaptability.
Enable Local Development: Use practices and patterns that allow components to be developed and run in local development environments.
API First: Treat APIs as a user-facing and critical part of the product. Design APIs first -- before implementation -- to ensure they meet a user need, are interoperable, and ease integration.
Test First: Write tests before code to clarify requirements, prevent defects early, and maintain code more easily.
User-Centric: Focus on end-user needs, preferences, and feedback in design and development. Endeavor to understand why a user needs a solution and how that solution serves the user's needs.
Don't Reinvent: Avoid "Not Invented Here" Syndrome by preferring to use existing products, services, and libraries instead of developing them in-house. Utilize existing solutions to save time and resources, focusing on unique value propositions. Buy -- not build.
You Build It, You Get the Alert: Put ownership of observability, monitoring, and the resulting alerts on the team that codes it. This ties responsibility to creators, encouraging careful development and a culture of accountability and responsiveness.
Graceful Degradation: Design the system to continue functioning even when some components fail, providing a lower-quality service instead of total failure.
Integrate Continuously: Automate the build and integration process, and use trunk-based development to enable frequent merges, ensuring that code changes are consistently tested and integrated into the shared repository.
Security by Design: Embed security considerations from the outset, including authorization, authentication, and encryption to protect data integrity and privacy.
Security by Depth: Implement multiple security controls throughout the system, layering protections at various levels such as network, host, application, and data, to enhance overall security and ensure that breaching one layer doesn't provide access to others.
Reactive Design: Build systems that respond gracefully to change, whether in load, user behavior, or underlying subsystem behavior.
Emphasize Interoperability: Focus on creating interfaces and components that can work seamlessly with various systems, promoting flexibility and adaptability.
Environment Agnosticism: Design the system to run across various environments and platforms, reducing dependencies on specific technologies or vendors.
Immutable Infrastructure: Once deployed, components are never modified. Use infrastructure as code and recreate components instead of modifying them, ensuring consistency and reducing potential discrepancies across environments.
These principles come from Art of Scalability:
N + 1 Design: Ensure that there is at least one additional instance of a system to handle failures. It's about building redundancy for reliability, and follows the rule of three: one for you, one for the customer, and one to fail.
Design for Rollback: Create systems that are backward compatible so that changes can be rolled back if problems arise. It's about being able to revert to a previous state if a failure or issue occurs after an update.
Design to Be Disabled: Create systems with the ability for parts of it to be disabled, especially in risky components. This enables more time to fix issues without the system going down. One way to do this is with feature flags.
Design to Be Monitored: Build systems that are self-diagnosing. This involves monitoring and logging the right information so that scalability issues can be addressed earlier.
Design for Multiple Live Sites: Plan for having multiple geographically dispersed data centers from the design phase. This allows for rapid deployment and helps in ensuring continuity during geographically isolated disasters.
Use Mature Technologies: Employ proven and reliable technology when high availability is required. New technologies may offer competitive advantages but can also introduce more risk.
Asynchronous Design: Design systems to act asynchronously instead of synchronously. Asynchronous systems are more tolerant of slowdowns and do not block the entire chain, enhancing scalability.
Stateless Systems: Avoid maintaining information about previous operations (state) as it can cost money, processing power, availability, and scalability. Designing stateless systems whenever possible simplifies scaling and enhances flexibility.
Scale Out, Not Up: Focus on horizontal scaling (adding more systems) instead of vertical scaling (upgrading a single system). It facilitates near-infinite scaling and is essential to leveraging the elasticity and auto-scaling features of modern infrastructure.
Design for at Least Two Axes of Scale: Think ahead to ensure systems are scalable, considering both present and future needs. Implement strategies like horizontal splits and customer segmentation to accommodate growth.
Buy When Non-Core: Focus on core competencies and purchase non-core components. Building should only occur if it creates a significant difference in the product or platform, enhancing competitive differentiation and shareholder value.
Use Commodity Hardware: Opt for cheaper, commodity hardware if it can scale horizontally and if the cost of ownership, even with higher failure rates, is lower than using higher-end hardware.
Build Small, Release Small, Fail Fast: Emphasize small, iterative development to minimize failure risk, save costs, and allow for quick course corrections if initiatives don't work.
Isolate Faults: Design systems to contain failures within isolated zones, similar to circuit breakers in electrical systems, to prevent a failure in one area from affecting others.
Automation over People: Prefer automation over human involvement for repetitive tasks. Automation is more consistent and often less expensive, making it more reliable and easier to tune for tasks like deployment, builds, testing, monitoring, and alerting.
25 more principle inspirations are available to paid subscribers below.
Keep reading with a 7-day free trial
Subscribe to Dev Details to keep reading this post and get 7 days of free access to the full post archives.