Good Practice v Bad Practice
In software engineering, “good practices” generally refer to techniques, approaches, and mindsets that result in reliable, maintainable, scalable, and high-quality software. In contrast, “bad practices” are those that make a codebase harder to understand, maintain, or evolve over time, or that lead to fragile systems and poor developer experience. Here are some key comparisons across various dimensions of the software development lifecycle:
1. Requirements and Planning
- Good Practice:
- Precise, Well-Defined Requirements: Working closely with stakeholders to gather comprehensive and testable requirements.
- Incremental and Iterative Planning: Using Agile or iterative approaches to break complex features into manageable chunks.
- Realistic Estimation and Prioritisation: Estimating effort based on historical data and complexity and prioritising tasks to deliver the highest value first.
- Bad Practice:
- Poor or Ambiguous Requirements: Starting development without fully understanding the problem domain leads to rework and delays.
- All-or-nothing Planning: Attempting to define and fix all details upfront in a waterfall-like manner without room for adaptation.
- Unrealistic Deadlines: Setting deadlines without regard to complexity or available resources, causing rushed, low-quality work.
2. Code Quality and Architecture
- Good Practice:
- Modular, Clean Architecture: Designing code so that it’s easy to reason about—using established architectural patterns (e.g., microservices, layered architecture, or hexagonal architecture).
- SOLID Principles: Applying principles that encourage single responsibility, open-closed design, and good separation of concerns.
- Consistent Coding Standards and Style: Using linters and formatting tools and following team-wide coding guidelines for readability.
- Meaningful Naming and Comments: Give descriptive names of variables, classes, and methods; document tricky logic when necessary.
- Bad Practice:
- Monolithic “God” Classes and Methods: Putting too many responsibilities into single files, making them hard to maintain and understand.
- Spaghetti Code: Tangled dependencies, lack of clear structure, and haphazard logic that make changes risky and error-prone.
- Inconsistent Naming and Formatting: Adopting random conventions or mixing styles, causing confusion for anyone reading or maintaining the code.
- Excessive or Non-existent Comments: Over-commenting obvious code or providing zero context on complex logic.
3. Testing and Quality Assurance
- Good Practice:
- Comprehensive Testing (Unit, Integration, End-to-End): Ensuring quality at multiple levels, from small components to entire system workflows.
- Test-Driven Development (TDD) or Behaviour-Driven Development (BDD): Writing tests first to clarify requirements and maintain a robust safety net.
- Continuous Integration (CI): Automatically running tests and static analysis on every commit to catch issues early.
- Bad Practice:
- Minimal or No Testing: Relying solely on manual testing or ignoring tests, resulting in fragile code.
- Unmaintained Tests: Keeping outdated tests that don’t match the current code, causing false positives or negatives.
- Skipping Integration or Stress Testing: Not testing how components interact at scale, leaving performance or reliability issues undiscovered until production.
4. Documentation and Communication
- Good Practice:
- Up-to-date documentation: Provide clear API docs, architectural decision records, and updated user guides as the system evolves.
- Knowledge Sharing: Using wikis, internal blogs, or lunch-and-learn sessions to disseminate best practices and decisions.
- Regular, Open Communication: Encouraging team discussions, daily standups, and retrospectives to uncover bottlenecks and continuously improve.
- Bad Practice:
- Outdated or Missing Documentation: Letting documentation lag behind code changes or failing to provide guidance.
- Knowledge Silos: Allowing only a few individuals to understand critical parts of the system makes the team vulnerable to turnover.
- Poor Communication of Changes: Not informing others about significant refactoring or design decisions, causing confusion and errors.
5. Tooling and Automation
- Good Practice:
- Automated Build and Deployment Pipelines: Ensuring reproducible, reliable, and frequent releases through CI/CD systems.
- Static Analysis and Code Quality Tools: Using linters, security scanners, and code coverage tools to maintain code health.
- Infrastructure-as-Code (IaC): Managing environments and configurations through version-controlled code.
- Bad Practice:
- Manual, Error-Prone Deployments: Relying on manual steps that increase the risk of mistakes.
- Ignoring Tooling Capabilities: Not using available industry-standard tools, resulting in repeated, time-consuming manual tasks.
- Inconsistent Environments: Relying on ad-hoc environment setups that differ between developers and production.
6. Performance and Scalability
- Good Practice:
- Regular Profiling and Optimisation: Continuously profiling applications to identify and address performance hotspots promptly.
- Scalable Design: When selecting architectures, caching strategies, and data stores, consider future growth and load.
- Caching and Load Testing: Using tools and strategies to handle increased traffic smoothly.
- Bad Practice:
- Premature or No Optimisation: Either optimising too early without data, creating unnecessary complexity, or ignoring performance until users complain.
- Ignoring Alerts and Metrics: Failing to monitor systems in production, thus missing early signs of performance regressions.
- One-Size-Fits-All Solutions: Applying the same architecture or technology stack to every problem without considering unique constraints.
7. Security and Compliance
- Good Practice:
- Security-First Mindset: Considering authentication, authorisation, and data protection from the outset.
- Regular Security Audits and Code Reviews: Encouraging peer reviews and automated scanning to detect vulnerabilities early.
- Compliance and Data Protection: Adhering to laws and regulations (e.g., GDPR, HIPAA), and employing encryption and access controls.
- Bad Practice:
- Hardcoding Credentials or Sensitive Data: Storing secrets in plaintext in code repositories or configuration files.
- Ignoring Security Updates: Delaying patches, allowing known vulnerabilities to persist in production.
- No Access Control or Monitoring: Failing to restrict access and log activity increases the risk of unauthorised use.
In Summary:
- Good practices foster clarity, maintainability, scalability, security, and a culture of continuous improvement and learning.
- Bad practices result in brittle, opaque, insecure codebases that are difficult to maintain, scale, or trust.
Adhering to good practices and avoiding bad ones improves software quality, boosts team morale, reduces technical debt, lowers long-term costs, and makes the product more resilient and valuable to its users.
Justin Barnes Consultancy Ltd
2 Peter Lane
York
YO1 8SW
01904 310231