Day 53 of #100DaysOfLearning
“Good Code, Bad Code: Think like a Software Engineer” by Tom Long

Day 53 of #100DaysOfLearning

Happy Coding!

Are you all having fun writing code today? Writing or thinking about code is a very creative process that is exciting and full of fun, right?

By the way, when you review the code you wrote, have you ever wondered what you were thinking when you wrote it?

Or, when you are developing in a team, have you ever felt that you could not communicate the intention of your code to the team members?

Perhaps you are writing bad code. I myself often write bad code. So I will read this book to learn what "Good code" is.

Good Code, Bad Code (manning.com)

Good Code, Bad Code (Japanese edition)

“Good Code, Bad Code: Think like a Software Engineer” is written by Tom Long, this book provides practical techniques for writing code that is robust, reliable, and easy for team members to understand and adapt.

Differentiating Good Code from Bad Code:

The distinction between “good code” and “bad code” varies depending on factors like context, role, and what you’re building. In this book, Tom Long, who served as a tech lead at Google, explains concepts and practical methods for creating code that meets the following four goals:

  • Functionality: Code should work correctly.
  • Robustness: It should continue functioning correctly over time.
  • Maintainability: It should be adaptable to changing requirements.
  • Avoiding Reinvention: Don’t reinvent the wheel unnecessarily.

To achieve these goals, the book outlines six pillars of code quality:

  • Readability: Write code that’s easy to understand.
  • Defensive Coding: Minimize unexpected behavior.
  • Avoiding Misuse: Write code that’s hard to misuse.
  • Modularity: Break code into manageable modules.
  • Reusability: Design code for reuse and generality.
  • Testability: Write testable code and test it effectively.

Let's dive deep into each concepts.

Functionality

Certainly! Let’s dive deeper into the concept of functionality in software engineering:

Definition:

Functionality refers to the ability of a software system or component to perform its intended tasks correctly and efficiently.

Importance:

Ensuring that code works as expected is fundamental. If code lacks functionality, it won’t fulfill its purpose, leading to bugs, errors, and unhappy users.

Key Aspects:

  • Correctness: Code should produce the expected output for all valid inputs. It should handle edge cases gracefully and avoid unexpected behavior.
  • Completeness: The code should cover all required features and use cases. Missing functionality can lead to incomplete or unusable software.
  • Robustness: Even when faced with unexpected inputs or conditions, the code should handle them gracefully without crashing or causing data corruption.
  • Performance: While functionality is crucial, it shouldn’t compromise performance. Efficient code ensures responsiveness and scalability.

Examples:

  • A calculator program should correctly perform arithmetic operations (addition, subtraction, etc.) for any valid input.
  • An e-commerce website should allow users to add items to their cart, proceed to checkout, and complete the purchase without errors.
  • A weather app should accurately display current weather conditions based on location and time.

Testing for Functionality:

  • Unit Tests: Write small, focused tests to verify individual functions or methods.
  • Integration Tests: Test interactions between different components or modules.
  • End-to-End Tests: Validate the entire system’s functionality from user input to output.

Common Pitfalls:

  • Assuming Correctness: Don’t assume code works; verify it through testing.
  • Ignoring Edge Cases: Consider boundary values, empty inputs, and unexpected scenarios.
  • Overlooking Dependencies: Ensure external libraries or services function as expected.
  • Incomplete Requirements: Understand user needs thoroughly to implement complete functionality.

Robustness

Definition:

Robustness refers to a software system’s ability to maintain correct behavior over time, even when faced with unexpected or adverse conditions.

Importance:

Code that lacks robustness can lead to system failures, data corruption, security vulnerabilities, and user dissatisfaction.

Key Aspects:

  • Error Handling: Robust code anticipates and handles errors gracefully. It doesn’t crash or produce incorrect results due to unexpected inputs or conditions.
  • Graceful Degradation: Even if certain features or components fail, the system should continue functioning with reduced capabilities.
  • Resilience: Robust software recovers from failures, whether caused by hardware issues, network glitches, or other external factors.
  • Security: A robust system defends against attacks, prevents unauthorized access, and handles malicious input.

Examples:

  • Input Validation: Robust code validates user input to prevent unexpected data (e.g., SQL injection, buffer overflow).
  • Graceful Shutdown: A server application should close connections and release resources properly during shutdown.
  • Retry Mechanisms: Robust network communication includes retries for failed requests.
  • Fallback Strategies: If an external service fails, a robust system might use cached data or an alternative service.

Testing for Robustness:

  • Stress Testing: Simulate heavy loads, extreme conditions, and resource exhaustion to assess system behavior.
  • Boundary Testing: Test edge cases (maximum/minimum values, empty inputs) to ensure robustness.
  • Security Testing: Verify how the system handles security threats.

Common Pitfalls:

  • Ignoring Edge Cases: Robust code considers all possible scenarios, not just the common ones.
  • Hardcoding Assumptions: Avoid assumptions about the environment or external services.
  • Lack of Logging: Proper logging helps diagnose issues and monitor system behavior.
  • Ignoring Failures: Don’t silently ignore errors; handle them explicitly.

Maintenance and Evolution:

Robust code remains adaptable as requirements change, technologies evolve, and new features are added.

Longevity:

A robust system continues to function correctly over years, adapting to changing environments and user needs.

Maintainability:

Definition:

Maintainability refers to a software system’s ability to undergo changes, updates, and enhancements without excessive effort or risk.

Importance:

Code that lacks maintainability becomes a liability over time. As requirements evolve, maintaining and extending the system becomes challenging.

Key Aspects:

  • Readability and Clarity: Maintainable code is easy to read and understand. Clear code reduces the learning curve for new developers and simplifies maintenance.
  • Modularity: Divide code into smaller, independent modules (functions, classes, etc.). Changes in one module shouldn’t affect others.
  • Documentation: Well-documented code explains its purpose, usage, and any design decisions. Comments, docstrings, and README files contribute to maintainability.
  • Avoiding Code Duplication: Repeated code (copy-paste) leads to maintenance nightmares. Use functions, libraries, and abstractions to avoid redundancy.
  • Test Coverage: Comprehensive test suites ensure that changes don’t break existing functionality. Automated tests catch regressions.
  • Version Control: Use version control systems (e.g., Git) to track changes, collaborate, and revert if needed.

Examples:

  • Adding Features: A maintainable system allows seamless addition of new features. Existing code doesn’t need major rewrites.
  • Bug Fixes: When a bug is discovered, maintainable code allows targeted fixes without affecting unrelated parts.
  • Refactoring: Refactoring (improving code structure without changing behavior) is easier in maintainable code.
  • Technology Upgrades: As libraries, frameworks, or languages evolve, maintainable code adapts smoothly.

Testing for Maintainability:

  • Unit Tests: Ensure that individual components work as expected.
  • Integration Tests: Verify interactions between modules.
  • Code Reviews: Peer reviews catch maintainability issues early.

Common Pitfalls:

  • Spaghetti Code: Unorganized, tightly coupled code is hard to maintain.
  • Magic Numbers and Strings: Avoid hardcoding values; use constants or configuration files.
  • Lack of Documentation: Inadequate comments make code cryptic.
  • Ignoring Warnings: Address compiler or linter warnings promptly.

Long-Term Perspective:

  • Think beyond the initial release. Prioritize maintainability to reduce technical debt.

Legacy Systems:

  • Even old codebases can improve maintainability through gradual refactoring.

Avoiding Reinvention

Definition:

This principle encourages developers to reuse existing solutions rather than creating new ones from scratch. It’s about leveraging existing libraries, frameworks, and best practices to save time, improve reliability, and maintain consistency.

Why Is It Important?:

  • Efficiency: Reusing existing code reduces development time. Why build something that already exists?
  • Quality: Established libraries and tools are often well-tested, optimized, and reliable.
  • Consistency: Using common solutions ensures uniformity across projects.

Examples:

  • Standard Libraries: Instead of writing your own sorting algorithm, use the one provided by your programming language.
  • Third-Party Libraries: Use open-source libraries for common tasks (e.g., database access, authentication, logging).
  • Design Patterns: Apply proven design patterns (e.g., Singleton, Observer) rather than inventing custom solutions.
  • Frameworks: Choose a web framework (e.g., Django, Express) rather than building an entire web server from scratch.

When to Reinvent?:

  • Specialized Needs: If existing solutions don’t meet specific requirements, consider custom implementations.
  • Learning Purposes: Reinventing the wheel can be educational, but it’s not suitable for production code.
  • Critical Components: For security-critical or performance-sensitive parts, carefully evaluate existing options.

Common Pitfalls:

  • Not Researching: Developers sometimes miss existing solutions due to lack of research.
  • Overengineering: Building complex custom solutions when simpler ones suffice.
  • Ignoring Community: Community-contributed libraries often improve over time.

Documentation and Licensing:

When using external code, understand its licensing terms and document dependencies.

Balance:

Strive for a balance between reusing existing code and maintaining flexibility.

Cultural Aspect:

Some cultures encourage reinvention as a way to learn deeply, but practical development often favors reuse.

Next Action

I have just started reading it, but I would like to read Good Code, Bad Code and digest the contents within myself over the three-day weekend starting tomorrow. (For me right now, every day is a holiday weekend. lol)


要查看或添加评论,请登录

Shinya Yanagihara的更多文章

  • Day 100 of #100DaysOfLearning

    Day 100 of #100DaysOfLearning

    I have mixed feelings about it, as if it was long and short. This is finally the 100th activity that I started with the…

    1 条评论
  • Day 99 of #100DaysOfLearning

    Day 99 of #100DaysOfLearning

    What a surprise! I found myself on the 99th day of the 100Days of Learning activity. Continuation is power, indeed.

  • Day 98 of #100DaysOfLearning

    Day 98 of #100DaysOfLearning

    How do you take notes when you study? There are some note-taking systems and techniques, such as Cornell note-taking…

  • Day 97 of #100DaysOfLearning

    Day 97 of #100DaysOfLearning

    Today is the fourth day of setting up a Windows environment. Today I finally get to set up my long-awaited development…

  • Day 96 of #100DaysOfLearning

    Day 96 of #100DaysOfLearning

    I am sure you are all aware that open source also has a license. I knew that, but I always managed my GitHub…

  • Day 95 of #100DaysOfLearning

    Day 95 of #100DaysOfLearning

    Today is the third day of building a new PC environment. Today I was mainly working on the configuration of Visual…

    2 条评论
  • Day 94 of #100DaysOfLearning

    Day 94 of #100DaysOfLearning

    It is no exaggeration to say that Windows is now Linux. I'm sure some of you don't know what I mean.

    2 条评论
  • Day 93 of #100DaysOfLearning

    Day 93 of #100DaysOfLearning

    In order to make a clean break with the past, I did a clean install of Windows 11 and began to create a clean…

  • Day 92 of #100DaysOfLearning

    Day 92 of #100DaysOfLearning

    Happy April Fool's Day! Today is April 1, which is April Fool's Day. Some of you may have been looking forward to April…

  • Day 91 of #100DaysOfLearning

    Day 91 of #100DaysOfLearning

    I actually haven't used a Mac since I left my last job and entered my career break period. I use Windows every day.

社区洞察

其他会员也浏览了