
The 8 Golden Rules of Writing Clean Functions - Clean Code In Kotlin
Philipp Lackner
Overview
This video outlines eight essential principles for writing clean, maintainable, and understandable Kotlin functions. These principles, while demonstrated in Kotlin, are broadly applicable to software development. They focus on improving code readability and reducing cognitive load by emphasizing single responsibility, avoiding side effects, adhering to the DRY principle, minimizing parameters, failing fast, using early returns, maintaining appropriate levels of detail, and judiciously using extension functions. Adhering to these rules leads to more robust, flexible, and easier-to-debug codebases.
Save this permanently with flashcards, quizzes, and AI chat
Chapters
- Functions should perform only one specific task or have a single reason to change.
- Violating SRP leads to functions that are hard to understand, increasing cognitive load.
- Multiple responsibilities in one function make code inflexible and difficult to reuse.
- Breaking down complex functions into smaller, single-purpose functions improves readability and maintainability.
- Side effects are actions a function takes that affect the outside world beyond its return value (e.g., database writes, analytics events).
- Functions should ideally be pure, meaning they only compute and return a value based on their inputs.
- Unintended side effects make it difficult to reason about function behavior and can lead to bugs.
- Separate functions for actions with side effects from those that only perform calculations.
- Avoid duplicating code throughout your project.
- Repeated code increases the effort required for maintenance, testing, and debugging.
- Extracting repeated logic into a single function or utility makes updates easier and less error-prone.
- Centralizing logic ensures consistency and reduces the chance of introducing bugs through manual copying.
- Functions with too many parameters increase cognitive load and are harder to use correctly.
- Group related parameters into data classes or objects.
- Passing a single object representing related data is more readable and maintainable than passing many individual parameters.
- This approach makes the function signature cleaner and clarifies the data requirements.
- Detect and report errors as early as possible within a function.
- Throw exceptions immediately when an error condition is identified.
- This prevents unnecessary computation and avoids executing code that will ultimately fail.
- Early failure detection simplifies debugging by pinpointing the source of the error quickly.
- Use early returns to exit a function as soon as a specific condition is met, especially for error or exit cases.
- This pattern reduces deep nesting of `if-else` statements, making code flatter and more readable.
- It clearly delineates the different exit paths of a function.
- Code following early returns can assume that preceding conditions have been met.
- Functions should operate at a level of abstraction consistent with their purpose.
- High-level functions should orchestrate actions using other functions, avoiding low-level implementation details.
- Low-level details (like credit card validation logic) should be encapsulated in separate, lower-level functions.
- This principle ensures that functions clearly represent the task they are intended to perform at their given abstraction layer.
- Extension functions in Kotlin allow adding functionality to existing classes without modifying them.
- Use extension functions when the receiver object (the class being extended) is the 'owner' of the operation.
- The receiver object should be the primary subject or target of the function's action.
- Avoid using extension functions when the receiver is merely a data carrier or not the true owner of the behavior.
Key takeaways
- Clean code is about making software easier to understand, maintain, and evolve.
- Functions should be small, focused, and perform a single, well-defined task.
- Minimize side effects to make functions predictable and easier to test.
- Avoid repeating code by extracting common logic into reusable functions.
- Group related data into objects or data classes to simplify function parameters.
- Handle errors as early as possible and use early returns to simplify control flow.
- Match the level of abstraction in functions to their intended purpose.
- Use Kotlin's extension functions thoughtfully to improve code readability when the receiver is the true owner of the functionality.
Key terms
Test your understanding
- What does it mean for a function to have a single responsibility, and why is this important?
- How can side effects in a function lead to unexpected behavior or bugs?
- Why is duplicating code detrimental to software maintenance, and what is the DRY principle?
- What is the recommended approach for handling functions with many related parameters?
- How does the 'fail fast' principle help in debugging and error handling?
- What are the benefits of using early returns in function design?
- How can you ensure a function operates at the appropriate level of detail or abstraction?
- Under what circumstances should you use Kotlin's extension functions?