Signing Git commits using SSH instead of GPG
TIL you can sign Git commits using SSH instead of GPG
This tip is 🏆, learned from my really smart colleague.
tl;dr;
To configure Git to use your key:
- Configure Git to use SSH for commit signing:
git config --global gpg.format ssh
- Specify which public SSH key to use as the signing key and change the
filename (
~/.ssh/examplekey.pub
) to the location of your key. The filename might differ, depending on how you generated your key:
git config --global user.signingkey ~/.ssh/examplekey.pub
To sign a commit:
- Use the
-S
flag when signing your commits:
git commit -S -m "My commit msg"
- Optional. If you don’t want to type the
-S
flag every time you commit, tell Git to sign your commits automatically:
git config --global commit.gpgsign true
Source: https://docs.gitlab.com/ee/user/project/repository/signed_commits/ssh.html
Embrace the power of Regex
Too often, while reviewing code, I’ll see examples like:
def extract_id_and_env(key: str) -> dict:
"""Extracts the object ID from `key`
`key` is a string like 'namespace_prefix_12345'
In some cases, `key` could also look like `namespace_prefix_12345_environment`
Returns a dict with the object ID, an integer
"""
parts = key.split('_')
parsed = {
'id': int(parts[2]),
'environment': parts[3] if len(parts) == 4 else None
}
return parsed
When I see this, I ask, “Why?”
Instead, this is my preferred way of handling this is to use a regex with named capture groups:
import re
KEY_PATTERN = re.compile(r'(?<namespace>[a-z]+)_(?<prefix>[a-z]+)_(?<object_id>\d+)(?:_(?P<environment>[a-z]+))?
def extract_key_components(key: str):
m = KEY_PATTERN.match(str)
parts = ['namespace', 'prefix', 'object_id', 'environment', ]
values = [m.group(part) for part in parts]
return values
In another example (contrived, but modified from a real world application), from a Django which serves both students and educators, and displays two different landing pages depending on the intent:
def login_view(request):
url = request.GET.get('next')
last_word = url.split("/")[-1]
is_student = True if last_word == 'scholarship' else False
template = 'login/student.html' if is_student else 'login/educator.html'
response = render_to_response(request, template)
return response
The problem with this code is not immediately apparent. It works. However, this code lacks robustness.
An arguably better approach:
import re
STUDENT_LOGIN_INTENT_PATTERNS = [
re.compile(r'^/path/to/(?P<some_id>\d+)/scholarship$'),
]
def is_login_intent_student(request):
is_student = False
next = request.GET.get('next')
for pattern in STUDENT_LOGIN_INTENT_PATTERNS:
if pattern.match(next):
is_student = True
break
return is_student
def login_view(request):
is_student = is_login_intent_student(request)
template = 'login/student.html' if is_student else 'login/educator.html'
response = render_to_response(request, template)
return response
In addition to the readability and maintainability of the regex approach, it is overall more robust, allowing the programmer to extract multiple components from the string all at once! This mitigates the need for updating the function in the future, if other parts of the string are needed later on (and it’s quite often that it would be the case!).
My preference for Regex over Split stems from:
- Somewhat related to the principle of https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/
- If code is wrong, it should fail catastrophically and loudly, not subtly or obscurely
- It’s hard to make a regex that looks maybe right? Either a regex is right, or obviously wrong. (It could also be that I have lots of experience using regexes, and can write them without lookup up references)
- OTOH, while
split
is conceptually easier to learn, for me, it’s hard or nearly impossible to see at a glance whether the code is write or wrong. For example, if you look at a block of code usingsplit
and various indexes, how would you instantly detect a possible OB1 (aka off-by-one error; https://en.wikipedia.org/wiki/Off-by-one_error)? Not possible. OB1s bugs are prevalent in software because the learning curve, and therefore barrier to entry, is low, so bugs are more likely to be introduced. - Regexes, OTOH, have a slightly higher learning curve, slightly higher barrier to entry, so those who use it tend not to make trivial mistakes
- If the code never has to update ever again, then, great!
split
is sufficient. If the next engineer has to update it, they would not necessarily benefit from the existing code, and would have to re-evaluate all of the code in their head to make sure indexes are right. - Maintaining a list of patterns, or regexes, encourages a Solve for N mentality, whereas using
split
encourages a “solve it quick and dirty mindset”
Use Fully Qualified datetime in Python
Whenever using the datetime
module in Python, a highly recommended
practice is to just import datetime
at the top of the file, and use
the fully-qualified module name in the code, as much as possible:
datetime.datetime
datetime.timedelta
datetime.date
If one does from datetime import datetime
, it’s hard to figure out
at-a-glance what datetime
is referring to in the middle of a
several-hundred-lines-of-code file.
For similar reasons, another common best practice in Python when using
the typing
module (https://docs.python.org/3/library/typing.html) is
to import is as import typing as T
or import typing as t
(e.g. https://github.com/pallets/flask/blob/cc66213e579d6b35d9951c21b685d0078f373c44/src/flask/app.py#L7; https://github.com/pallets/werkzeug/blob/3115aa6a6276939f5fd6efa46282e0256ff21f1a/src/werkzeug/wrappers/request.py#L4)
Get Is the Worst Function Prefix Ever
tl;dr;
Unless the function you are writing is a getter
(which complements a setter
), please avoid naming methods get_
.
get_
lacks descriptiveness, precision, and is boring.
Rationale
Software engineers are supposed to creative, and get
is possible the least creative function name possible.
Too often, I see codebases cluttered with get_
methods throughout, when the implementations of those methods do things far more complex than simply reading or getting a value from an object.
When half of a file with hundreds of lines of code are all named get_*
, this makes code difficult to read, scan, and reason about. (Future blog posts will address code that is easy vs difficult to reason about.)
Alternatives
Since much of the world’s software (historically) has been produced from within English-speaking countries and by English-speaking programmers and software engineering teams, please allow me introduce to you the robustness of the English language.
While my hope and expectation is not for every software engineer to have a Shakespearean command over English vocabulary, I do think that it is quite tenable to learn a few prefixes to help codebases become more manageable and pleasing to read.
Without further ado, these are my suggestions:
build_
calculate_
extract_
fetch_
look_up_
/retrieve_
format_
/transform_
Below are examples and sample code, in Python (my language of choice).
build_
When to use it: Use this prefix for a method which takes in some data, and builds a more complex structure as a result.
Analogy: You have multiple loose LEGO bricks, and want to assemble those pieces to build a structure out of that.
Bad
def get_response(color, food, location):
response = {
'color': color,
'food': food,
'location': location,
}
return response
Good
def build_response(color, food, location):
response = {
'color': color,
'food': food,
'location': Location(location),
}
return response
Usage
response = build_response('green', 'eggs and ham', 'in a car')
# Result:
# {
# 'color': 'green',
# 'food': 'eggs and ham',
# 'location': {
# 'name': 'in a car'
# },
# }
calculate_
When to use it: When you have some data, and some formula is applied to calculate a result.
Analogy: If it’s not doable via “mental math,” and needs to be calculated.
Setup
items = [
{
'color': 'green',
'food': 'eggs and ham',
'location': {
'name': 'in a car'
},
'price_cents': 1525,
},
{
'color': 'red',
'food': 'hot chili peppers',
'location': {
'name': 'with a mouse'
},
'price_cents': 299,
},
{
'color': 'orange',
'food': 'carrots',
'location': {
'name': 'here or there'
},
'price_cents': 399,
},
]
Bad:
def get_total(items, unit='cents'):
total_cents = sum([item['price_cents'] for item in items])
if unit == 'cents':
total = total_cents
elif unit == 'dollars':
total = float((Decimal(total_cents) / Decimal(100)).quantize(Decimal('1.00')))
return total
Good:
def calculate_total(items, unit='cents'):
total_cents = sum([item['price_cents'] for item in items])
if unit == 'cents':
total = total_cents
elif unit == 'dollars':
total = float((Decimal(total_cents) / Decimal(100)).quantize(Decimal('1.00')))
return total
A method named calculate_
will mentally prepare the engineer to be extra careful and meticulous when maintaining this code, because the goal is to be accurate and precise.
extract_
When to use it: When you need one, or a few pieces of information, from a more complex structure.
Analogy: You have a plain rock (diamond ore, gold ore) which is relatively useless on the surface, and want to extract the valuable contents (diamonds, gold).
Setup
response = {
'color': 'green',
'food': 'eggs and ham',
'location': {
'name': 'in a car'
}
}
Bad
def get_color(response):
return response['color']
def get_location_name(response):
return response['location']['name']
# Usage
color = get_color(response)
location_name = get_location_name(response)
Better
response = {
'color': 'green',
'food': 'eggs and ham',
'location': {
'name': 'in a car'
}
}
def extract_color(response):
return response['color']
def extract_location_name(response):
return response['location']['name']
# Usage
color = extract_color(response)
location_name = extract_location_name(response)
Great
Consider using object-oriented programming:
class Response:
def __init__(self, raw_response):
self.raw_response = raw_response
@property
def color(self):
return self.raw_response['color']
@property
def location_name(self):
return self.raw_response['location']['name']
# Usage
r = Response(response)
color = r.color
location_name = r.location_name
fetch_
When to use it: Use this prefix when the method makes an HTTP call or other API call. Inspired by fetch
from JavaScript (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
Analogy: If you have a warm and cuddly friendly dog, and you’re at the park playing the good ol’ game of fetch; the object you’re intending to retrieve is accessible to you, but not immediately within reach.
Bad
def get_story_details(story_id):
response = requests.get(f'https://api.example.com/story/{story_id}/details')
return response
If the method is named get_
, there’s no way to distinguish at a glance whether this function calls out to another server/service.
Whenever the flow of your code leaves your control (like making an API call), there is inherent risk and potential for errors to occur (e.g. “What if the remote API goes down?”)
Good
def fetch_story_details(story_id):
response = requests.get(f'https://api.example.com/story/{story_id}/details')
return response
By naming methods that make API calls to remote resources withfetch_
, you allow engineers to quickly identify risky sections of code at a glance, without requiring them to read the details of a function – and this saves time – allowing a flywheel effect of: by writing code faster, teams produce more code / fix bugs more quickly, delivering more business value, enabling these teams to hire more team members, to build more products….
So, if I see a method named fetch_
, I can immediately make mental note to make these sections of code more resilient (such as with try
and except
error handling, retry logic with exponential backoffs, etc).
look_up_
When to use it: Use this prefix when the goal of the method is to retrieve data that was previous stored in a local storage, like a database.
Analogy: There is a single piece of information you want to retrieve from among a larger collection, like *looking up** the defintion of a word in a glossary.
Setup
# MySQL
| id | item | price_cents |
----------------------------------------
| 1 | eggs and ham | 1525 |
| 2 | hot chili peppers | 299 |
| 3 | eggs and ham | 399 |
Bad
def get_price(item):
sql = connect()
q = sql.query('items').where(item=item)
price = q.execute()['price_cents']
return price
Better
def look_up_price(item):
sql = connect()
q = sql.query('items').where(item=item)
price = q.execute()['price_cents']
return price
By naming a method look_up
, you mentally prepare the next engineer who reviews this code that this method involves some form of database retrieval, and they can also keep in mind the performance characteristics of database retrieval.
Best
Use the database repository design pattern.
# repos/items.py
class ItemRepo:
def get(self, item: str) -> Record:
sql = connect()
q = sql.query('items').where(item=item)
record = q.execute()
return record
def look_up_price(self, item: str) -> float:
record = self.get(item)
price = record['price_cents']
return price
format_
/ transform_
When to use it: When the desire output is a derivative of the inputs, or a metamorphosis such that output is not recognizable from the original form, but only to a connoiseur.
Analogy: When the input is uncooked potatoes and the output is mashed potatoes, you are transforming the raw ingredients into a useful end-product.
Bad
def get_mashed_potato(raw_potato):
boiled_potato = boil(raw_potato)
mashed_potato = mash(boiled_potato)
return mashed_potato
Good
def transform_potato(raw_potato, form_factor):
result = raw_potato
if form_factor == 'raw':
result = raw_potato
elif form_factor == 'baked':
result = bake(raw_potato)
elif form_factor == 'boiled':
result = boil(raw_potato)
elif form_factor == 'mashed':
result = mash(transform_potato(raw_potato, 'boiled'))
return result
Alternatively, format_potato
with the same function body above would work.
Conclusion
Please, for the love of all things proper, think twice before creating another method with the prefix name get_
, and use one of the better alternatives: build_
, extract_
, fetch_
, look_up_
, retrieve_
, transform_
.
I promise you – your future self and your teammates will thank you!
Feedback
Agree? Disagree? Love it? Hate it?
Please leave comments or drop me a line!
14 Rules for Being a Godly Employee
Back in 2016, I came across this article: https://thecripplegate.com/14-rules-for-being-a-godly-employee/
Recently, I’ve resolved to start my work week by reviewing the 14 rules:
- Rule #1 – Eagerly start the day’s main work
- Rule #2 – Do not murmur at your busyness or the shortness of time but buy up the time all around.
- Rule #3 – Never murmur when correspondence is brought in.
- Rule #4 – Never exaggerate duties by seeming to suffer under the load, but treat all responsibilities as liberty and gladness.
- Rule #5 – Never call attention to crowded work or trivial experiences.
- Rule #6 – Before confrontation or censure, obtain from God a real love for the one at fault. Know the facts; be generous in your judgment. Otherwise, how ineffective, how unintelligible or perhaps provocative your well-intentioned censure may be.
- Rule #7 – Do not believe everything you hear; do not spread gossip.
- Rule #8 – Do not seek praise, gratitude, respect, or regard for past service.
- Rule #9 – Avoid complaining when your advice or opinion is not consulted, or having been consulted, set aside.
- Rule #10 – Never allow yourself to be placed in favorable contrast with anyone.
- Rule #11 – Do not press conversation to your own needs and concerns.
- Rule #12 – Seek no favors, nor sympathies, do not ask for tenderness, but receive what comes.
- Rule #13 – Bear the blame; do not share or transfer it.
- Rule #14 – Give thanks when credit for your own work or idea is given to another.