File size: 14,811 Bytes
203d1e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# A Concise, Opinionated Guide to Writing Good Code (with Python examples)

This guide summarizes core principles for writing clean, maintainable, and effective code. It's opinionated and rule-based, designed to provide clear direction for junior developers. Adhering to these rules will help you build better software and become a more valuable team member. Python examples are provided for clarity.

## 1. Naming Matters Immensely

* **Rule:** Use intention-revealing names.
    * **Don't:** `d = (datetime.now() - start_date).days`
    * **Do:** `elapsed_time_in_days = (datetime.now() - start_date).days`
* **Rule:** Avoid disinformation.
    * **Don't:** `account_list = {"id": 1, "name": "Alice"}` (It's a dictionary, not a list)
    * **Do:** `account_data = {"id": 1, "name": "Alice"}` or `account_dict = ...`
* **Rule:** Use pronounceable and searchable names.
    * **Don't:** `genymdhms = datetime.now().strftime('%Y%m%d%H%M%S')`
    * **Do:** `generation_timestamp = datetime.now().strftime('%Y%m%d%H%M%S')`
* **Rule:** Be consistent.
    * **Don't:** Using `fetch_user_data`, `getUserInfo`, `retrieve_client_details` in the same project.
    * **Do:** Consistently use one style, e.g., `get_user_data`, `get_order_info`, `get_product_details`.

## 2. Functions Should Be Small and Focused

* **Rule:** Functions must do **one thing**.
    * **Don't:**
        ```python
        def process_user_data(user_id):
            # Fetches data
            response = requests.get(f"/api/users/{user_id}")
            user_data = response.json()
            # Validates data
            if not user_data.get("email"):
                raise ValueError("Email missing")
            # Saves data
            db.save(user_data)
            # Sends notification
            send_email(user_data["email"], "Welcome!")
            return user_data
        ```
    * **Do:** Break it down:
        ```python
        def fetch_user_data(user_id):
            response = requests.get(f"/api/users/{user_id}")
            response.raise_for_status() # Raise HTTP errors
            return response.json()

        def validate_user_data(user_data):
            if not user_data.get("email"):
                raise ValueError("Email missing")
            # ... other validations

        def save_user_data(user_data):
            db.save(user_data)

        def send_welcome_email(email_address):
            send_email(email_address, "Welcome!")

        def register_user(user_id):
            user_data = fetch_user_data(user_id)
            validate_user_data(user_data)
            save_user_data(user_data)
            send_welcome_email(user_data["email"])
            return user_data
        ```
* **Rule:** Functions must be **small**. (The "Do" example above also illustrates this).
* **Rule:** Minimize function arguments.
    * **Don't:** `def create_user(name, email, password, dob, address, phone, role, is_active): ...`
    * **Do:**
        ```python
        class UserProfile:
            def __init__(self, name, email, dob, address, phone):
                # ... initialization ...

        def create_user(profile: UserProfile, password: str, role: str, is_active: bool = True):
             # ... use profile attributes ...
        ```
        Or pass a dictionary:
        ```python
        def create_user(user_details: dict):
            # Access details via user_details['name'], user_details['email'] etc.
            # Consider using TypedDict for better structure if using Python 3.8+
            ...
        ```
* **Rule:** Avoid side effects where possible.
    * **Don't (Hidden Side Effect):**
        ```python
        user_list = []
        def add_user_if_valid(name, email):
            if "@" in email:
                user_list.append({"name": name, "email": email}) # Modifies global state
                return True
            return False
        ```
    * **Do (Explicit):**
        ```python
        def create_user_record(name, email):
            if "@" not in email:
                raise ValueError("Invalid email")
            return {"name": name, "email": email}

        # Usage
        try:
            new_user = create_user_record("Bob", "bob@example.com")
            user_list.append(new_user) # State change happens outside the function
        except ValueError as e:
            print(f"Error: {e}")
        ```

## 3. Comments Are for "Why," Not "What"

* **Rule:** Comment the "Why," not the "What."
    * **Don't:**
        ```python
        # Check if user is eligible
        if age >= 18 and country == "US": # This just repeats the code
            is_eligible = True
        ```
    * **Do:**
        ```python
        # User must be a legal adult in the US to qualify for this specific offer.
        if age >= 18 and country == "US":
            is_eligible = True
        ```
* **Rule:** Do **not** leave commented-out code.
    * **Don't:**
        ```python
        def calculate_total(items):
            total = 0
            for item in items:
                total += item['price']
            # tax = total * 0.10 # Old tax calculation
            # total += tax
            total *= 1.10 # Apply 10% tax
            return total
        ```
    * **Do:** Remove the commented lines. Use Git history if you need to see the old calculation.
        ```python
        def calculate_total(items):
            total = sum(item['price'] for item in items)
            total *= 1.10 # Apply 10% tax
            return total
        ```
* **Rule:** Keep comments up-to-date. (Self-explanatory - if the logic changes, update or remove the comment).
* **Rule:** Avoid redundant comments.
    * **Don't:**
        ```python
        count = 0 # Initialize count
        count += 1 # Increment count
        ```
    * **Do:** Just the code is enough.
        ```python
        count = 0
        count += 1
        ```

## 4. Formatting and Structure Enhance Readability

* **Rule:** Use a consistent style guide (e.g., PEP 8 for Python). Use tools like `Black`, `Flake8`, `isort`.
    * **Don't:** Inconsistent spacing, line lengths, import orders.
    * **Do:** Code automatically formatted by tools like `Black`.
* **Rule:** Top-down narrative.
    * **Don't:** Define helper functions *before* the main function that uses them, forcing the reader to jump around.
    * **Do:**
        ```python
        def main_process():
            data = _fetch_data()
            result = _process_data(data)
            _save_result(result)

        # --- Helper functions defined below ---
        def _fetch_data(): ...
        def _process_data(data): ...
        def _save_result(result): ...
        ```
        *(Note: Leading underscore `_` often indicates internal/helper functions)*
* **Rule:** Keep related concepts vertically close. (The example above also shows this).
* **Rule:** Use whitespace.
    * **Don't:**
        ```python
        def process(a,b,c):
            x=a+b
            y=x*c
            if y>10:
                print("Large")
            else:
                print("Small")
            z=y-a
            return z
        ```
    * **Do:**
        ```python
        def process(a, b, c):
            intermediate_value = a + b
            final_value = intermediate_value * c

            if final_value > 10:
                print("Large")
            else:
                print("Small")

            adjusted_value = final_value - a
            return adjusted_value
        ```

## 5. Keep It Simple (KISS & YAGNI)

* **Rule:** KISS (Keep It Simple, Stupid).
    * **Don't:** Using complex metaprogramming or obscure language features when a simple loop or conditional would suffice.
    * **Do:** Prefer straightforward, readable solutions.
* **Rule:** YAGNI (You Ain't Gonna Need It).
    * **Don't:** Adding configuration options, database fields, or API endpoints for features that *might* be needed in the future but aren't required now.
    * **Do:** Implement only what's necessary for the current requirements.
* **Rule:** Avoid premature optimization.
    * **Don't:** Spending hours micro-optimizing a function with string concatenations before profiling to see if it's even a bottleneck.
    * **Do:** Write clean code first. If performance is an issue (measure it!), profile and optimize the specific hotspots. Often, a better algorithm beats micro-optimization.

## 6. Don't Repeat Yourself (DRY)

* **Rule:** Avoid duplication.
    * **Don't:**
        ```python
        def process_file_a(path):
            # 10 lines of validation logic
            if not valid: return None
            # Process file A specific logic
            ...

        def process_file_b(path):
            # Same 10 lines of validation logic copied here
            if not valid: return None
            # Process file B specific logic
            ...
        ```
    * **Do:**
        ```python
        def _validate_input(path):
            # 10 lines of validation logic
            return is_valid

        def process_file_a(path):
            if not _validate_input(path): return None
            # Process file A specific logic
            ...

        def process_file_b(path):
            if not _validate_input(path): return None
            # Process file B specific logic
            ...
        ```

## 7. Handle Errors Gracefully

* **Rule:** Use exceptions over error codes.
    * **Don't:**
        ```python
        def divide(a, b):
            if b == 0:
                return -1 # Error code
            return a / b

        result = divide(10, 0)
        if result == -1:
            print("Error: Division by zero")
        ```
    * **Do:**
        ```python
        def divide(a, b):
            if b == 0:
                raise ValueError("Cannot divide by zero")
            return a / b

        try:
            result = divide(10, 0)
        except ValueError as e:
            print(f"Error: {e}")
        ```
* **Rule:** Provide context with errors.
    * **Don't:** `raise Exception("Error!")`
    * **Do:** `raise ValueError(f"Invalid user ID format: '{user_id_str}'")`

## 8. Test Your Code

* **Rule:** Write unit tests (using frameworks like `pytest` or `unittest`).
    * **Don't:** Skipping tests because the code "looks simple."
    * **Do:**
        ```python
        # Example using pytest
        from my_module import add

        def test_add_positive_numbers():
            assert add(2, 3) == 5

        def test_add_negative_numbers():
            assert add(-1, -1) == -2

        def test_add_mixed_numbers():
            assert add(5, -3) == 2
        ```
* **Rule:** Test behavior, not implementation.
    * **Don't:** Writing a test that checks if a specific private helper method (`_helper`) was called.
    * **Do:** Writing a test that checks if the public method produces the correct output or state change, regardless of which internal helpers were used.
* **Rule:** Keep tests clean, readable, and fast. (Apply the same principles from this guide to your test code).

## 9. Practice Continuous Refactoring

* **Rule:** Follow the Boy Scout Rule.
    * **Don't:** Seeing a poorly named variable or a slightly complex block of code and leaving it because "it works."
    * **Do:** Taking a few moments to rename the variable or extract a small function to improve clarity before committing your primary change.
* **Rule:** Refactoring is part of development. (This is a mindset, less about specific code examples).

## 10. Optimize for Readability

* **Rule:** Code is read more than written.
    * **Don't:** Using overly clever one-liners or complex list comprehensions that are hard to decipher.
        ```python
        # Clever but potentially hard to read
        result = [x**2 for x in range(10) if x % 2 == 0 and x > 3]
        ```
    * **Do:** Prioritize clarity, even if it means slightly more verbose code.
        ```python
        result = []
        for x in range(10):
            is_even = x % 2 == 0
            is_greater_than_3 = x > 3
            if is_even and is_greater_than_3:
                result.append(x**2)
        # Or a more readable comprehension if appropriate
        result = [x**2 for x in range(4, 10, 2)] # Clearer range
        ```

## 11. Python-Specific Best Practices

* **Rule:** Embrace Pythonic idioms.
    * **Use List Comprehensions (when clear):** Prefer `squares = [x*x for x in numbers]` over manual `for` loop appends for simple transformations.
    * **Use Context Managers (`with` statement):** Ensure resources like files or network connections are properly closed.
        ```python
        # Don't
        f = open("myfile.txt", "w")
        try:
            f.write("Hello")
        finally:
            f.close()

        # Do
        with open("myfile.txt", "w") as f:
            f.write("Hello")
        # File is automatically closed here, even if errors occur
        ```
    * **Iterate Directly:** Iterate over sequences directly instead of using index manipulation.
        ```python
        # Don't
        for i in range(len(my_list)):
            print(my_list[i])

        # Do
        for item in my_list:
            print(item)

        # Do (if index is needed)
        for i, item in enumerate(my_list):
            print(f"Index {i}: {item}")
        ```
* **Rule:** Use Type Hinting (Python 3.5+). Improves readability, enables static analysis tools (`mypy`), and clarifies intent.
    ```python
    # Don't
    def greet(name):
        print("Hello " + name)

    # Do
    def greet(name: str) -> None:
        print("Hello " + name)

    def add(a: int, b: int) -> int:
        return a + b
    ```
* **Rule:** Use Virtual Environments (`venv`). Isolate project dependencies to avoid conflicts between projects. Always create and activate a virtual environment before installing packages (`pip install ...`).
* **Rule:** Prefer f-strings (Python 3.6+) for string formatting. They are generally more readable and often faster than `.format()` or `%` formatting.
    ```python
    name = "Alice"
    age = 30

    # Don't (older styles)
    print("Name: %s, Age: %d" % (name, age))
    print("Name: {}, Age: {}".format(name, age))

    # Do
    print(f"Name: {name}, Age: {age}")
    ```
* **Rule:** Understand Mutable Default Arguments. Be wary of using mutable types (like lists or dicts) as default function arguments, as they are shared across calls.
    ```python
    # Don't (potential bug)
    def add_item(item, my_list=[]):
        my_list.append(item)
        return my_list

    list1 = add_item(1) # [1]
    list2 = add_item(2) # [1, 2] - Unexpected!

    # Do
    def add_item(item, my_list=None):
        if my_list is None:
            my_list = []
        my_list.append(item)
        return my_list

    list1 = add_item(1) # [1]
    list2 = add_item(2) # [2] - Correct