Pull to refresh

Thoughts and short notes (in go) after reading «Clean Code»

Level of difficultyEasy
Reading time4 min
Views1.4K

1. Clean Code

The gist: Clean code is more than just working code; it's code that other developers can easily read, understand, and modify.

// Not so clean
func p(a []int) {
  // ...some cryptic code...
}
// Clean
func sortNumbers(numbers []int) {
  // sorting logic...
}

2. Meaningful Names

The gist: Use descriptive and specific names that reveal your intention.

// Confusing
var d int // elapsed time in days
// Clear
var elapsedTimeInDays int
// Even clearer in context
func calculateElapsedTime(startDate, endDate time.Time) int {
  return int(endDate.Sub(startDate).Hours() / 24)
}

3. Functions

The gist: Functions should do one thing, be small, and have no side effects.

// Too complex
func handleHttpRequest(r http.Request) {
// parsing, logging, and handling...
}
// Decomposed into focused functions
func parseRequest(r http.Request) RequestData {...}
func logRequest(data RequestData) {...}
func handleRequest(data RequestData) Response {...}

4. Comments

The gist: Good code mostly speaks for itself.

// Unnecessary
// Check if user is valid
if user.IsValid() {...}
// Clear code
if user.IsValid() {...} // No comment needed

5. Formatting

The gist: Consistent and thoughtful formatting makes your code easier to read and understand.

// Inconsistent
func foo() {
  var x int
  x=3
  fmt.Println(x)
}
// Consistent
func formatNumber(number int) string {
  formatted := fmt.Sprintf("%03d", number)
  return formatted
}

6. Objects and Data Structures

The gist: Objects hide their data behind abstractions and expose functions to operate on that data. Data structures expose their data and have no significant behavior.

// Data structure
type Rectangle struct {
  Width  float64
  Height float64
}
// Object-like behavior
type Shape interface {
  Area() float64
}
func (r Rectangle) Area() float64 {
  return r.Width * r.Height
}

7. Error Handling

The gist: Treat error handling as a primary concern, not an afterthought.

func connectDatabase(connectionString string) (*sql.DB, error) {
  db, err := sql.Open("postgres", connectionString)
  if err != nil {
    return nil, fmt.Errorf("error connecting to database: %v", err)
  }
  // Additional connection verification...
  return db, nil
}

8. Boundaries

The gist: Respect boundaries between different layers and services by keeping interchanges well-defined and clean.

// Interfacing with an external package
func readConfigFile(path string) (Config, error) {
  var config Config
  data, err := ioutil.ReadFile(path)
  if err != nil {
    return config, err
  }
  err = json.Unmarshal(data, &config)
  return config, err
}

9. Unit Tests

The gist: Tests keep your code flexible, maintainable, and understandable.

func TestCalculateTotal(t *testing.T) {
  total := calculateTotal([]int{10, 20, 30})
  if total != 60 {
    t.Errorf("Expected 60, got %d", total)
  }
}

10. Classes (Structs and Interfaces in Go)

The gist: Keep your data structures small, focused, and with a clear purpose.

type Server interface {
  Start()
  Stop()
}
type HttpServer struct {
  // Server configuration fields
}
func (h *HttpServer) Start() {
  // start server
}
func (h *HttpServer) Stop() {
  // stop server
}

11. Systems

The gist: Building clean systems involves organizing code into layers and managing dependencies carefully.
Conceptual Example:
Organize your code into packages with clear purposes. Use interfaces to abstract implementation details and dependencies.

12. Emergence

The gist: Simple design, refactoring, and a focus on building well-crafted code lead to emergent behavior.
Conceptual Example:
Regularly refactor and simplify your code. Remove duplication, improve names, and break large functions into smaller, more focused ones.

13. Concurrency

The gist: Concurrency is tricky; keep it separate and well-managed.

func processConcurrently(data []int) {
  var wg sync.WaitGroup
  for _, value := range data {
    wg.Add(1)
    go func(val int) {
      defer wg.Done()
      processValue(val)
    }(value)
  }
  wg.Wait()
}

14. Successive Refinement

The gist: Code should be continuously improved upon.
Conceptual Example:
Take an existing function or module and improve it: clarify names, split large functions, reduce dependencies, and add tests.

15. JUnit Internals

The gist: Understanding how good frameworks and libraries work can inspire you to write better code.
Conceptual Example:
Look into the source code of a well-respected Go package to understand its structure and design decisions.

16. Refactoring SerialDate

The gist: Refactoring is an essential tool for maintaining and improving the structure of existing code.
Conceptual Example:
Identify a complex module or function in your codebase and step through a refactoring process to improve it.

17. Smells and Heuristics

The gist: Recognize common "smells" in your code that indicate problems and understand heuristics for solving them.
Conceptual Example:
Review your code for "smells" such as rigidity, needless complexity, and duplication. Apply heuristics like the Single Responsibility Principle and DRY (Don't Repeat Yourself) to improve it.

Conclusion

Applying clean code principles isn't just about following rules; it's about developing a mindset. The more you practice writing clear, concise, and maintainable code, the more natural it will become. Just making a program that works isn't enough; it should also be tidy and understandable. Remember, clean code benefits your future self and your teammates, so always aim for clarity and simplicity. Keep it tidy!

Tags:
Hubs:
Rating0
Comments0

Articles