This chapter talks about the foundation of software engineering. It explains fundamental concepts such as writing code, testing, designing, refactoring, migrating, and scaling that are necessary to know for every software engineer. Internalizing them will help you build software that works well, lasts long, and is easy to maintain.
Chapter Contents
- 2.1 Writing Code
- 2.1.1 Adhering to Coding Principles and Patterns
- 2.1.2 Common Coding Pitfalls
- 2.1.3 Balancing Specialization and Generalization
- 2.1.4 Hiding Complexity
- 2.1.5 Benchmarking your Code
- 2.1.6 Ensuring Robustness and Security
- 2.1.7 Avoiding Copy/Pastes
- 2.1.8 Maintaining Consistency
- 2.1.9 Documentation and Code Reviews
- 2.2 Algorithms and Data Structures
- 2.2.1 Common Data Structures
- 2.2.1.1 Arrays
- 2.2.1.2 Lists
- 2.2.1.3 Stacks
- 2.2.1.4 Queues
- 2.2.1.5 Sets
- 2.2.1.6 Maps
- 2.2.1.7 Priority Queues & Heaps
- 2.2.1.8 Trees and Tries
- 2.2.2 Programming Language
- 2.2.3 Asymptotic Analysis
- 2.2.4 Low-level Computations
- 2.2.5 Latency and Speed
- 2.2.6 Common Algorithms
- 2.2.6.1 Sorting
- 2.2.6.2 Hashing
- 2.2.6.3 Recursion
- 2.2.6.4 Searching
- 2.2.6.5 Divide and Conquer
- 2.2.6.6 Backtracking
- 2.2.6.7 Greedy
- 2.2.6.8 Randomized
- 2.2.6.9 Dynamic Programming
- 2.2.6.10 Brute Force
- 2.2.7 Problem Solving
- 2.2.8 Enhancing Algorithmic Skills
- 2.2.1 Common Data Structures
- 2.3 Testing
- 2.3.1 Types of Software Testing
- 2.3.1.1 Unit Tests
- 2.3.1.2 Integration Tests
- 2.3.1.3 End-to-End Tests
- 2.3.1.4 Performance Testing
- 2.3.1.5 Stress Testing
- 2.3.1.6 Smoke Tests
- 2.3.1.7 Regression Testing
- 2.3.1.8 Chaos Testing
- 2.3.1.9 Monte Carlo Simulation
- 2.3.1.10 Quality Assurance
- 2.3.1 Types of Software Testing
- 2.4 Design Patterns
- 2.4.1 Creational Patterns
- 2.4.1.1 Singleton
- 2.4.1.2 Factory
- 2.4.1.3 Abstract Factory
- 2.4.1.4 Builder
- 2.4.1.5 Object Pool
- 2.4.1.6 Prototype
- 2.4.2 Structural Patterns
- 2.4.2.1 Adapter
- 2.4.2.2 Bridge
- 2.4.2.3 Composite
- 2.4.2.4 Decorator
- 2.4.2.5 Facade
- 2.4.2.6 Flyweight
- 2.4.2.7 Proxy
- 2.4.3 Behavioral Patterns
- 2.4.3.1 Chain of Responsibility
- 2.4.3.2 Command
- 2.4.3.3 Interpreter
- 2.4.3.4 Iterator
- 2.4.3.5 Mediator
- 2.4.3.6 Memento
- 2.4.3.7 Null Object
- 2.4.3.8 Observer
- 2.4.3.9 State
- 2.4.3.10 Strategy
- 2.4.3.11 Template Method
- 2.4.3.12 Visitor
- 2.4.1 Creational Patterns
- 2.5 Architecture Design
- 2.5.1 Requirements Gathering
- 2.5.1.1 Functional Design
- 2.5.2 Domain-Driven Design
- 2.5.3 Non-functional Requirements
- 2.5.4 Architectural Patterns
- 2.5.4.1 Layered Architecture
- 2.5.4.2 Event-driven Architecture
- 2.5.4.3 Microservices Architecture
- 2.5.4.4 Serverless Architecture
- 2.5.5 Deployment Considerations
- 2.5.1 Requirements Gathering
- 2.6 Design Review
- 2.6.1 Pre-Design Preparation
- 2.6.2 Creating Design Documents
- 2.6.3 Presenting Design Documents
- 2.6.4 Reviewing Design Documents
- 2.6.5 Benefits
- 2.6.6 Deciding on a Design Document
- 2.7 TDD
- 2.7.1 Benefits and Challenges of TDD
- 2.7.2 How to TDD Effectively
- 2.8 Programming Environment
- 2.8.1 Shell
- 2.8.2 Vim Editor
- 2.8.3 Linters
- 2.8.4 Version Control
- 2.8.5 Code Generation
- 2.8.6 Machine
- 2.8.7 Build Tools and Package Managers
- 2.9 Scaling Services
- 2.9.1 Vertical Scaling
- 2.9.2 Balancing the Workload
- 2.9.3 Caching
- 2.9.4 Queuing
- 2.9.5 Horizontal Scaling
- 2.9.5.1 Partitioning
- 2.9.5.2 Consistency
- 2.9.5.3 Consensus
- 2.9.5.4 Hot Partitions
- 2.9.5.5 Key-Value Stores
- 2.9.5.6 CAP Theorem
- 2.9.6 ACID
- 2.9.6.1 Atomicity
- 2.9.6.2 Consistency
- 2.9.6.3 Isolation
- 2.9.6.4 Durability
- 2.9.7 Scaling Operations
- 2.9.7.1 Operational Load
- 2.9.7.2 Deployment and Monitoring
- 2.9.7.3 Disaster Recovery
- 2.10 Migrations
- 2.10.1 Why Migrate
- 2.10.2 Planning Migration
- 2.10.3 Executing Migration
- 2.11 Graceful Degradation
- 2.11.1 Dependency Graph
- 2.11.2 Queuing
- 2.11.3 Throttling
- 2.11.4 Load Shedding
- 2.11.5 Browning Out
- 2.11.6 Autoscaling
- 2.11.7 Stress Testing
- 2.11.8 Chaos Engineering
- 2.12 Code Review
- 2.12.1 Author
- 2.12.2 Reviewer
- 2.12.3 Code Review Process
- 2.13 Hacking
- 2.13.1 Cases for Hacking
- 2.13.2 Malicious Hacking
- 2.13.2.1 Encryption
- 2.13.2.2 Code Injection
- 2.13.2.3 Buffer Overflows
- 2.13.2.4 Access Control
- 2.13.2.5 Vulnerabilities in Dependencies
- 2.13.3 Human Factor
- 2.13.3.1 Social Engineering
- 2.13.3.2 Plain Text Documents
- 2.13.3.3 Browser and Application Extensions
- 2.14 Hackathons
- 2.15 Tech Debt
- 2.15.1 Tech Debt Types
- 2.15.2 Indicators of Tech Debt
- 2.15.3 Engineering Health
- 2.15.4 Partner Collaboration
- 2.16 Technical Writing
- 2.16.1 Why Write
- 2.16.1.1 Brain Dump
- 2.16.1.2 Technical Discussions
- 2.16.1.3 Share Updates
- 2.16.1.4 Teach Others
- 2.16.1.5 Share Plans
- 2.16.1.6 Engage with Your Community
- 2.16.1.7 Articles and Papers
- 2.16.1.8 Reflect
- 2.16.2 Advance Your Career
- 2.16.3 Writing Muscle
- 2.16.4 Writing Tips
- 2.16.1 Why Write
- 2.17 Documentation
- 2.17.1 Documenting Rationale
- 2.17.2 Ways of Documenting
- 2.17.3 Documentation Culture
- 2.18 Engineering Principles
- 2.18.1 Ship Frequently
- 2.18.2 Conduct Incident Debrief
- 2.18.3 Design in Public
- 2.18.4 Hire, Grow, and Retain The Best
- 2.18.5 Review all Code
- 2.18.6 Build CI/CD Pipelines
- 2.18.7 Run What You Build
- 2.18.8 Write the Definition of Done
- 2.19 Pair Programming
- 2.20 CI/CD
- 2.21 Refactoring
- 2.21.1 Refactoring Indicators
- 2.21.2 Refactoring Techniques
- 2.21.2.1 Remove if Unused
- 2.21.2.2 Rename
- 2.21.2.3 Make it Testable
- 2.21.2.4 Extract Method
- 2.21.2.5 Extract Interface
- 2.21.2.6 Replace Type Code with Subclasses
- 2.21.2.7 Introduce Parameter Object
- 2.21.2.8 Introduce Gateway
- 2.21.2.9 Replace Inheritance with Delegation
- 2.22 Whiteboarding
- 2.23 SOLID
- 2.23.1 Single Responsibility Principle
- 2.23.2 Open-Closed Principle
- 2.23.3 Liskov Substitution Principle
- 2.23.4 Interface Segregation Principle
- 2.23.5 Dependency Inversion Principle