Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 186 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ you are expecting a string as the argument.
**Good**:

```python
def create_micro_brewery(name: str="Hipster Brew Co."):
def create_micro_brewery(name: str = "Hipster Brew Co."):
slug = hashlib.sha1(name.encode()).hexdigest()
# etc.
```
Expand Down Expand Up @@ -431,3 +431,188 @@ message.send()
```

**[⬆ back to top](#table-of-contents)**

### Functions should only be one level of abstraction

When you have more than one level of abstraction, your function is usually doing too

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this is too similar to section Functions should do one thing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Adapted from https://github.com/jupeter/clean-code-php#functions-should-only-be-one-level-of-abstraction -- it does appear to be a rephrasing of Functions should do only one thing.

much. Splitting up functions leads to reusability and easier testing.

**Bad:**

```python
def parse_better_js_alternative(code: str) -> None:
regexes = [
# ...
]

statements = regexes.split()
tokens = []
for regex in regexes:
for statement in statements:
# ...

ast = []
for token in tokens:
# Lex.

for node in ast:
# Parse.
```

**Good:**

```python
def parse_better_js_alternative(code: str) -> None:
tokens = tokenize(code)
syntax_tree = parse(tokens)

for node in syntax_tree:
# Parse.


def tokenize(code: str) -> list:
REGEXES = [
# ...
]

statements = code.split()
tokens = []
for regex in REGEXES:
for statement in statements:
# Append the statement to tokens.

return tokens


def parse(tokens: list) -> list:
syntax_tree = []
for token in tokens:
# Append the parsed token to the syntax tree.

return syntax_tree
```

**[⬆ back to top](#table-of-contents)**

### Don't use flags as function parameters

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


Flags tell your user that this function does more than one thing. Functions
should do one thing. Split your functions if they are following different code
paths based on a boolean.

**Bad:**

```python
from pathlib import Path

def create_file(name: str, temp: bool) -> None:
if temp:
Path('./temp/' + name).touch()
else:
Path(name).touch()
```

**Good:**

```python
from pathlib import Path

def create_file(name: str) -> None:
Path(name).touch()

def create_temp_file(name: str) -> None:
Path('./temp/' + name).touch()
```

**[⬆ back to top](#table-of-contents)**

### Avoid side effects

A function produces a side effect if it does anything other than take a value in
and return another value or values. A side effect could be writing to a file,
modifying some global variable, or accidentally wiring all your money to a
stranger.

Now, you do need to have side effects in a program on occasion - for example, like
in the previous example, you might need to write to a file. In these cases, you
should centralize and indicate where you are incorporating side effects. Don't have
several functions and classes that write to a particular file - rather, have one
(and only one) service that does it.

The main point is to avoid common pitfalls like sharing state between objects
without any structure, using mutable data types that can be written to by anything,
and not centralizing where your side effects occur. If you can do this, you will be
happier than the vast majority of other programmers.

**Bad:**

```python
# Global variable referenced by following function.
# If another function used this name, now it'd be an array and could break.
name = 'Ryan McDermott'

def split_into_first_and_last_name() -> None:
global name
name = name.split()

split_into_first_and_last_name()

print(name) # ['Ryan', 'McDermott']
```

**Good:**
```python
def split_into_first_and_last_name(name: str) -> None:
return name.split()

name = 'Ryan McDermott'
new_name = split_into_first_and_last_name(name)

print(name) # 'Ryan McDermott'
print(new_name) # ['Ryan', 'McDermott']
```

**[⬆ back to top](#table-of-contents)**

### Don't write to global functions

Polluting globals is a bad practice in many languages because you could clash

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this one. The function config() is not global, but defined at the module level, which is fine to do.

However, you could do the following:

globals()['foo'] = 1

This would modify the global namespace, which is seldom necessary and would be considered bad practice (use modules instead).

Feel free to open a new pull request if I have misunderstood this section.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right. Subsection adapted from https://github.com/jupeter/clean-code-php#dont-write-to-global-functions. I suppose PHP requires that you explicitly declare namespaces, so the same module-scoping policy doesn't apply?

with another library, and the user of your library would be none-the-wiser
until they get an exception in production. Let's think about an example: what
if you wanted to hace a configuration array? You could write a global function like
`config()`, but it could clash with another library that tried to do the same thing.

**Bad:**

```python
def config() -> dict:
return {
'foo': 'bar',
}
```

**Good:**

```python
from typing import Any, Dict, Optional

class Configuration
def __init__(self, configuration: Dict[str, Any]):
self._configuration = configuration

def get(self, key: str) -> Optional[str]:
return self._configuration.get(key)
```

Load configuration and create instance of `Configuration` class:

```python
configuration = Configuration(
{
'foo': 'bar',
}
)
```
And now, you may use this instance of `Configuration` in your application.

**[⬆ back to top](#table-of-contents)**