Kill It With Fire
Responsible Modernization
- Modernization definition: Define modernization clearly as improving systems to support future business needs, not just replacing old technology
- System understanding: Develop deep understanding of the existing system before making changes; don’t assume old means bad
- Business value focus: Focus modernization efforts on delivering business value, not technical elegance
- Risk management: Manage risk through incremental changes, testing, and maintaining recovery paths
- Data-driven approach: Make decisions based on data about the system, not assumptions or emotions
- Experimentation culture: Build a culture of safe experimentation to test hypotheses about the system
- Technical debt balance: Balance addressing technical debt with delivering new features
- Blame avoidance: Avoid blaming previous engineers; they made rational decisions with the information they had
- Legacy appreciation: Appreciate that legacy systems represent institutional knowledge, not just outdated technology
- Change management: Manage the human side of change as carefully as the technical side
Understanding the System
- Comprehensive assessment: Assess legacy systems from multiple angles: performance, architecture, security, and business value
- Environment mapping: Map the entire environment, including dependencies, interfaces, and data flows
- User interaction: Understand how users actually interact with the system, not just how it’s supposed to work
- Performance bottlenecks: Identify true performance bottlenecks through measurement, not speculation
- Security evaluation: Evaluate security holistically, not just through vulnerability scanning
- Documentation creation: Create documentation as you learn about the system; don’t expect it to exist already
- Knowledge extraction: Extract knowledge from system behavior, support tickets, and institutional memory
- System boundaries: Define clear system boundaries to scope modernization efforts effectively
- Data lineage: Trace data lineage to understand how information flows through the system
- Stakeholder mapping: Identify all stakeholders, not just direct users and developers
Deliberate Technical Decision Making
- Decision framework: Establish a clear decision-making framework for technical choices
- Trade-off analysis: Analyze trade-offs explicitly, documenting what you’re gaining and giving up
- Reversibility consideration: Consider the reversibility of decisions; prefer reversible options when uncertainty is high
- Opinionated tools: Choose appropriately opinionated tools that guide developers toward best practices
- Technology selection: Select technologies based on organizational fit, not just technical merits
- Vendor relationship: Approach vendor relationships strategically, considering lock-in and support
- Technical debt management: Manage technical debt deliberately, distinguishing between different types
- Architecture evolution: Evolve architecture incrementally rather than complete rewrites
- Platform thinking: Think in terms of platforms that can evolve, not point solutions
- Standard adherence: Adopt standards for consistency, but don’t let them paralyze progress
Modernization Approach
- Incremental strategy: Implement changes incrementally, delivering value at each step
- Refactoring approach: Refactor in small, measurable steps rather than big-bang rewrites
- Parallel running: Run old and new systems in parallel during transition periods
- Feature flags: Use feature flags to control the rollout of new functionality
- Backward compatibility: Maintain backward compatibility to support gradual transition
- Deprecation policy: Establish clear policies for deprecating old functionality
- Migration assistance: Provide support and tools to help users migrate
- Performance measurement: Measure performance before and after changes to validate improvements
- Rollback planning: Always have a clear rollback plan for when things go wrong
- Success definition: Define success in terms of business outcomes, not technical implementation
Working with Legacy Code
- Test harness creation: Build test harnesses to safely modify legacy code
- Surface comprehension: First understand the surface of the code before diving deep
- Strangler pattern application: Apply the strangler pattern to gradually replace functionality
- Code archaeology: Practice code archaeology to understand the why behind decisions
- Dead code identification: Identify and safely remove dead code
- Code quality improvement: Improve code quality gradually through consistent refactoring
- Knowledge transfer: Transfer knowledge from legacy systems to new implementations
- Technical debt categorization: Categorize technical debt to prioritize what to address
- Refactoring sequencing: Sequence refactoring to minimize risk and maximize value
- Documentation creation: Create documentation as you understand the code
People and Organizational Aspects
- Team structure: Structure teams to support modernization, with clear ownership and accountability
- Skill development: Develop skills for both maintaining legacy systems and building new ones
- Institutional knowledge: Preserve institutional knowledge during transitions
- Transition management: Manage the transition period with clear roles and responsibilities
- Incentive alignment: Align incentives to reward successful modernization, not just new features
- Psychological safety: Create psychological safety for engineers working on risky changes
- Stakeholder management: Manage stakeholder expectations throughout the modernization process
- Knowledge sharing: Establish mechanisms for sharing knowledge across teams
- Career development: Support career development for engineers working on legacy systems
- Cross-training importance: Implement cross-training to prevent knowledge silos
Patterns and Anti-patterns
- Big bang rewrite risk: Avoid big bang rewrites; they almost always fail
- Resume-driven development: Watch for resume-driven development pushing unnecessary new tech
- Second-system syndrome: Beware of second-system syndrome leading to over-engineering
- Premature optimization: Avoid premature optimization; measure before optimizing
- Dependency management: Manage dependencies carefully to prevent cascade failures
- Technical debt acknowledgment: Acknowledge that all systems accumulate technical debt
- Feature parity trap: Avoid the feature parity trap; not all legacy features need to be migrated
- Conway’s law awareness: Be aware of Conway’s law - system design reflects organizational structure
- Real user feedback: Get feedback from real users, not just stakeholders
- Root cause analysis: Perform thorough root cause analysis on failures to improve the system
Technical Operations
- Monitoring implementation: Implement comprehensive monitoring before making changes
- Incident management: Establish clear incident management processes
- Deployment automation: Automate deployments to reduce human error
- Configuration management: Manage configuration systematically, preferably as code
- Environmental parity: Create development environments that match production
- Database migration planning: Plan database migrations carefully with data verification
- Failover testing: Test failover procedures regularly
- Capacity planning: Plan capacity based on actual measurements and growth projections
- Performance testing: Conduct regular performance testing of both old and new systems
- Technical documentation: Maintain technical documentation that explains why, not just how
Measuring Success
- Success metrics: Define clear metrics for modernization success tied to business outcomes
- Leading indicators: Identify leading indicators of successful modernization
- User satisfaction: Measure user satisfaction and experience, not just technical metrics
- Business impact: Track the business impact of modernization efforts
- Technical debt reduction: Measure reduction in technical debt
- Incident reduction: Track reduction in incidents and resolution time
- Developer productivity: Monitor developer productivity and satisfaction
- Deployment frequency: Measure deployment frequency and lead time
- Security improvement: Track security improvements and vulnerability reduction
- System health: Monitor overall system health and stability
Key Takeaways
- Incremental modernization: Modernize systems incrementally rather than through risky rewrites
- Deep understanding: Develop deep understanding of existing systems before making changes
- Data-driven decisions: Make decisions based on data and measurement, not assumptions
- Risk management: Manage risk through incremental changes and maintaining recovery paths
- Business value focus: Focus modernization efforts on delivering business value
- Legacy respect: Respect the knowledge embedded in legacy systems
- People consideration: Consider the human aspects of modernization as carefully as technical ones
- Testing importance: Test thoroughly before, during, and after changes
- Documentation creation: Create documentation as you learn about the system
- Technical debt balance: Balance addressing technical debt with delivering new features