cli-dev-tip 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli_dev_tip-0.1.0.dist-info/METADATA +223 -0
- cli_dev_tip-0.1.0.dist-info/RECORD +19 -0
- cli_dev_tip-0.1.0.dist-info/WHEEL +4 -0
- cli_dev_tip-0.1.0.dist-info/entry_points.txt +2 -0
- cli_dev_tip-0.1.0.dist-info/licenses/LICENSE +21 -0
- dev_tip/__init__.py +0 -0
- dev_tip/ai/__init__.py +55 -0
- dev_tip/ai/cache.py +114 -0
- dev_tip/ai/gemini.py +28 -0
- dev_tip/ai/openrouter.py +37 -0
- dev_tip/ai/prompt.py +60 -0
- dev_tip/ai/provider.py +26 -0
- dev_tip/cli.py +274 -0
- dev_tip/config.py +98 -0
- dev_tip/data/tips.yaml +2070 -0
- dev_tip/history.py +46 -0
- dev_tip/hook.py +192 -0
- dev_tip/prefetch.py +98 -0
- dev_tip/tips.py +45 -0
dev_tip/data/tips.yaml
ADDED
|
@@ -0,0 +1,2070 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Python tips
|
|
3
|
+
# =============================================================================
|
|
4
|
+
|
|
5
|
+
- id: python-001
|
|
6
|
+
topic: python
|
|
7
|
+
title: Use enumerate() instead of range(len())
|
|
8
|
+
body: >
|
|
9
|
+
Instead of `for i in range(len(items))`, use `for i, item in enumerate(items)`.
|
|
10
|
+
It's more Pythonic and gives you both the index and value.
|
|
11
|
+
example: |
|
|
12
|
+
for i, name in enumerate(names, start=1):
|
|
13
|
+
print(f"{i}. {name}")
|
|
14
|
+
level: beginner
|
|
15
|
+
source: https://docs.python.org/3/library/functions.html#enumerate
|
|
16
|
+
|
|
17
|
+
- id: python-002
|
|
18
|
+
topic: python
|
|
19
|
+
title: f-strings support expressions and formatting
|
|
20
|
+
body: >
|
|
21
|
+
f-strings can contain any valid Python expression, including method calls,
|
|
22
|
+
math, and format specifiers.
|
|
23
|
+
example: |
|
|
24
|
+
name = "world"
|
|
25
|
+
print(f"{'hello':>10} {name.upper()}")
|
|
26
|
+
print(f"pi = {3.14159:.2f}")
|
|
27
|
+
level: beginner
|
|
28
|
+
source: https://docs.python.org/3/reference/lexical_analysis.html#f-strings
|
|
29
|
+
|
|
30
|
+
- id: python-003
|
|
31
|
+
topic: python
|
|
32
|
+
title: Use pathlib for file paths
|
|
33
|
+
body: >
|
|
34
|
+
The pathlib module provides an object-oriented interface for filesystem paths.
|
|
35
|
+
It's cross-platform and more readable than os.path.
|
|
36
|
+
example: |
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
config = Path.home() / ".config" / "app" / "settings.toml"
|
|
39
|
+
config.parent.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
level: intermediate
|
|
41
|
+
source: https://docs.python.org/3/library/pathlib.html
|
|
42
|
+
|
|
43
|
+
- id: python-004
|
|
44
|
+
topic: python
|
|
45
|
+
title: Walrus operator for assignment expressions
|
|
46
|
+
body: >
|
|
47
|
+
The := operator assigns and returns a value in one expression.
|
|
48
|
+
Useful in while loops and comprehensions.
|
|
49
|
+
example: |
|
|
50
|
+
# Read lines until empty
|
|
51
|
+
while (line := input("Enter text: ")) != "":
|
|
52
|
+
print(f"You said: {line}")
|
|
53
|
+
level: intermediate
|
|
54
|
+
source: https://peps.python.org/pep-0572/
|
|
55
|
+
|
|
56
|
+
- id: python-005
|
|
57
|
+
topic: python
|
|
58
|
+
title: Use collections.Counter for counting
|
|
59
|
+
body: >
|
|
60
|
+
Counter is a dict subclass for counting hashable objects. It has helpful
|
|
61
|
+
methods like most_common() and supports arithmetic operations.
|
|
62
|
+
example: |
|
|
63
|
+
from collections import Counter
|
|
64
|
+
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
|
|
65
|
+
counts = Counter(words)
|
|
66
|
+
print(counts.most_common(2)) # [('apple', 3), ('banana', 2)]
|
|
67
|
+
level: beginner
|
|
68
|
+
source: https://docs.python.org/3/library/collections.html#collections.Counter
|
|
69
|
+
|
|
70
|
+
- id: python-006
|
|
71
|
+
topic: python
|
|
72
|
+
title: Use contextlib for custom context managers
|
|
73
|
+
body: >
|
|
74
|
+
The @contextmanager decorator lets you write context managers as simple
|
|
75
|
+
generator functions instead of full classes with __enter__/__exit__.
|
|
76
|
+
example: |
|
|
77
|
+
from contextlib import contextmanager
|
|
78
|
+
import time
|
|
79
|
+
|
|
80
|
+
@contextmanager
|
|
81
|
+
def timer(label):
|
|
82
|
+
start = time.perf_counter()
|
|
83
|
+
yield
|
|
84
|
+
print(f"{label}: {time.perf_counter() - start:.3f}s")
|
|
85
|
+
|
|
86
|
+
with timer("query"):
|
|
87
|
+
run_query()
|
|
88
|
+
level: intermediate
|
|
89
|
+
source: https://docs.python.org/3/library/contextlib.html
|
|
90
|
+
|
|
91
|
+
- id: python-007
|
|
92
|
+
topic: python
|
|
93
|
+
title: Use dataclasses for simple data containers
|
|
94
|
+
body: >
|
|
95
|
+
The @dataclass decorator auto-generates __init__, __repr__, __eq__ and more.
|
|
96
|
+
Saves boilerplate for classes that mainly hold data.
|
|
97
|
+
example: |
|
|
98
|
+
from dataclasses import dataclass
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class Point:
|
|
102
|
+
x: float
|
|
103
|
+
y: float
|
|
104
|
+
label: str = ""
|
|
105
|
+
level: beginner
|
|
106
|
+
source: https://docs.python.org/3/library/dataclasses.html
|
|
107
|
+
|
|
108
|
+
- id: python-008
|
|
109
|
+
topic: python
|
|
110
|
+
title: Use __slots__ for memory-efficient classes
|
|
111
|
+
body: >
|
|
112
|
+
Defining __slots__ prevents creation of __dict__ per instance,
|
|
113
|
+
reducing memory usage significantly for classes with many instances.
|
|
114
|
+
example: |
|
|
115
|
+
class Point:
|
|
116
|
+
__slots__ = ("x", "y")
|
|
117
|
+
def __init__(self, x, y):
|
|
118
|
+
self.x = x
|
|
119
|
+
self.y = y
|
|
120
|
+
level: advanced
|
|
121
|
+
source: https://docs.python.org/3/reference/datamodel.html#slots
|
|
122
|
+
|
|
123
|
+
- id: python-009
|
|
124
|
+
topic: python
|
|
125
|
+
title: Use functools.lru_cache for memoization
|
|
126
|
+
body: >
|
|
127
|
+
The @lru_cache decorator caches function results based on arguments.
|
|
128
|
+
Great for expensive computations with repeated inputs.
|
|
129
|
+
example: |
|
|
130
|
+
from functools import lru_cache
|
|
131
|
+
|
|
132
|
+
@lru_cache(maxsize=128)
|
|
133
|
+
def fib(n):
|
|
134
|
+
if n < 2:
|
|
135
|
+
return n
|
|
136
|
+
return fib(n - 1) + fib(n - 2)
|
|
137
|
+
level: intermediate
|
|
138
|
+
source: https://docs.python.org/3/library/functools.html#functools.lru_cache
|
|
139
|
+
|
|
140
|
+
- id: python-010
|
|
141
|
+
topic: python
|
|
142
|
+
title: Use structural pattern matching
|
|
143
|
+
body: >
|
|
144
|
+
Python 3.10 introduced match/case statements for structural pattern matching.
|
|
145
|
+
It can destructure objects, match types, and handle complex conditions.
|
|
146
|
+
example: |
|
|
147
|
+
match command.split():
|
|
148
|
+
case ["quit"]:
|
|
149
|
+
exit()
|
|
150
|
+
case ["go", direction]:
|
|
151
|
+
move(direction)
|
|
152
|
+
case ["get", item] if item in inventory:
|
|
153
|
+
pick_up(item)
|
|
154
|
+
case _:
|
|
155
|
+
print("Unknown command")
|
|
156
|
+
level: advanced
|
|
157
|
+
source: https://peps.python.org/pep-0636/
|
|
158
|
+
|
|
159
|
+
- id: python-011
|
|
160
|
+
topic: python
|
|
161
|
+
title: Use itertools for efficient looping
|
|
162
|
+
body: >
|
|
163
|
+
The itertools module provides memory-efficient tools for working with
|
|
164
|
+
iterators. chain, product, groupby, and islice cover most use cases.
|
|
165
|
+
example: |
|
|
166
|
+
from itertools import chain, islice
|
|
167
|
+
|
|
168
|
+
# Flatten multiple lists
|
|
169
|
+
all_items = list(chain([1, 2], [3, 4], [5]))
|
|
170
|
+
|
|
171
|
+
# Take first 5 items from any iterable
|
|
172
|
+
first_five = list(islice(range(1000000), 5))
|
|
173
|
+
level: intermediate
|
|
174
|
+
source: https://docs.python.org/3/library/itertools.html
|
|
175
|
+
|
|
176
|
+
- id: python-012
|
|
177
|
+
topic: python
|
|
178
|
+
title: Use dict.get() with a default value
|
|
179
|
+
body: >
|
|
180
|
+
dict.get(key, default) returns the default instead of raising KeyError
|
|
181
|
+
when the key is missing. Cleaner than try/except for simple lookups.
|
|
182
|
+
example: |
|
|
183
|
+
config = {"debug": True}
|
|
184
|
+
# Instead of: config["timeout"] if "timeout" in config else 30
|
|
185
|
+
timeout = config.get("timeout", 30)
|
|
186
|
+
level: beginner
|
|
187
|
+
source: https://docs.python.org/3/library/stdtypes.html#dict.get
|
|
188
|
+
|
|
189
|
+
- id: python-013
|
|
190
|
+
topic: python
|
|
191
|
+
title: Use zip() to iterate multiple sequences
|
|
192
|
+
body: >
|
|
193
|
+
zip() pairs up elements from multiple iterables. In Python 3.10+
|
|
194
|
+
use strict=True to catch length mismatches.
|
|
195
|
+
example: |
|
|
196
|
+
names = ["Alice", "Bob", "Charlie"]
|
|
197
|
+
scores = [95, 87, 92]
|
|
198
|
+
|
|
199
|
+
for name, score in zip(names, scores, strict=True):
|
|
200
|
+
print(f"{name}: {score}")
|
|
201
|
+
level: beginner
|
|
202
|
+
source: https://docs.python.org/3/library/functions.html#zip
|
|
203
|
+
|
|
204
|
+
- id: python-014
|
|
205
|
+
topic: python
|
|
206
|
+
title: Use __init_subclass__ for plugin patterns
|
|
207
|
+
body: >
|
|
208
|
+
__init_subclass__ runs when a class is subclassed. Perfect for auto-registering
|
|
209
|
+
plugins without metaclasses or decorators.
|
|
210
|
+
example: |
|
|
211
|
+
class Plugin:
|
|
212
|
+
registry = {}
|
|
213
|
+
def __init_subclass__(cls, name=None, **kwargs):
|
|
214
|
+
super().__init_subclass__(**kwargs)
|
|
215
|
+
Plugin.registry[name or cls.__name__] = cls
|
|
216
|
+
|
|
217
|
+
class JSONLoader(Plugin, name="json"):
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
print(Plugin.registry) # {"json": <class 'JSONLoader'>}
|
|
221
|
+
level: advanced
|
|
222
|
+
source: https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__
|
|
223
|
+
|
|
224
|
+
- id: python-015
|
|
225
|
+
topic: python
|
|
226
|
+
title: Use typing.Protocol for structural subtyping
|
|
227
|
+
body: >
|
|
228
|
+
Protocol defines interfaces by structure, not inheritance. A class matches
|
|
229
|
+
if it has the right methods — no need to explicitly subclass.
|
|
230
|
+
example: |
|
|
231
|
+
from typing import Protocol
|
|
232
|
+
|
|
233
|
+
class Closable(Protocol):
|
|
234
|
+
def close(self) -> None: ...
|
|
235
|
+
|
|
236
|
+
def cleanup(resource: Closable) -> None:
|
|
237
|
+
resource.close()
|
|
238
|
+
|
|
239
|
+
# Any object with a close() method works — no inheritance needed
|
|
240
|
+
level: advanced
|
|
241
|
+
source: https://docs.python.org/3/library/typing.html#typing.Protocol
|
|
242
|
+
|
|
243
|
+
# =============================================================================
|
|
244
|
+
# Git tips
|
|
245
|
+
# =============================================================================
|
|
246
|
+
|
|
247
|
+
- id: git-001
|
|
248
|
+
topic: git
|
|
249
|
+
title: Use git stash to save work in progress
|
|
250
|
+
body: >
|
|
251
|
+
`git stash` saves your uncommitted changes and reverts to a clean working
|
|
252
|
+
directory. Use `git stash pop` to restore them later.
|
|
253
|
+
example: |
|
|
254
|
+
git stash
|
|
255
|
+
git checkout main
|
|
256
|
+
# do some work
|
|
257
|
+
git checkout feature-branch
|
|
258
|
+
git stash pop
|
|
259
|
+
level: beginner
|
|
260
|
+
source: https://git-scm.com/docs/git-stash
|
|
261
|
+
|
|
262
|
+
- id: git-002
|
|
263
|
+
topic: git
|
|
264
|
+
title: Interactive rebase to clean up commits
|
|
265
|
+
body: >
|
|
266
|
+
Use `git rebase -i` to squash, reorder, or edit commits before pushing.
|
|
267
|
+
Keeps your branch history clean and logical.
|
|
268
|
+
example: |
|
|
269
|
+
git rebase -i HEAD~3
|
|
270
|
+
# In the editor, change 'pick' to 'squash' or 'fixup'
|
|
271
|
+
level: intermediate
|
|
272
|
+
source: https://git-scm.com/docs/git-rebase
|
|
273
|
+
|
|
274
|
+
- id: git-003
|
|
275
|
+
topic: git
|
|
276
|
+
title: Use git bisect to find bugs
|
|
277
|
+
body: >
|
|
278
|
+
`git bisect` performs a binary search through your commit history to find
|
|
279
|
+
the exact commit that introduced a bug.
|
|
280
|
+
example: |
|
|
281
|
+
git bisect start
|
|
282
|
+
git bisect bad # current commit is broken
|
|
283
|
+
git bisect good v1.0.0 # this tag was working
|
|
284
|
+
# git checks out middle commit, you test, then:
|
|
285
|
+
git bisect good # or git bisect bad
|
|
286
|
+
level: advanced
|
|
287
|
+
source: https://git-scm.com/docs/git-bisect
|
|
288
|
+
|
|
289
|
+
- id: git-004
|
|
290
|
+
topic: git
|
|
291
|
+
title: Amend the last commit message
|
|
292
|
+
body: >
|
|
293
|
+
Made a typo in your last commit message? Use `--amend` to fix it.
|
|
294
|
+
Only do this before pushing.
|
|
295
|
+
example: |
|
|
296
|
+
git commit --amend -m "fix: correct validation logic"
|
|
297
|
+
level: beginner
|
|
298
|
+
source: https://git-scm.com/docs/git-commit
|
|
299
|
+
|
|
300
|
+
- id: git-005
|
|
301
|
+
topic: git
|
|
302
|
+
title: Use git log --oneline for a compact history
|
|
303
|
+
body: >
|
|
304
|
+
The --oneline flag shows each commit on a single line with just the
|
|
305
|
+
short hash and message. Add --graph for a visual branch view.
|
|
306
|
+
example: |
|
|
307
|
+
git log --oneline --graph --all
|
|
308
|
+
level: beginner
|
|
309
|
+
source: https://git-scm.com/docs/git-log
|
|
310
|
+
|
|
311
|
+
- id: git-006
|
|
312
|
+
topic: git
|
|
313
|
+
title: Cherry-pick commits from other branches
|
|
314
|
+
body: >
|
|
315
|
+
`git cherry-pick` applies the changes from specific commits onto your
|
|
316
|
+
current branch. Useful for backporting fixes.
|
|
317
|
+
example: |
|
|
318
|
+
git cherry-pick abc1234
|
|
319
|
+
# Apply multiple commits
|
|
320
|
+
git cherry-pick abc1234 def5678
|
|
321
|
+
level: intermediate
|
|
322
|
+
source: https://git-scm.com/docs/git-cherry-pick
|
|
323
|
+
|
|
324
|
+
- id: git-007
|
|
325
|
+
topic: git
|
|
326
|
+
title: Use git worktree for parallel work
|
|
327
|
+
body: >
|
|
328
|
+
`git worktree` lets you check out multiple branches simultaneously in
|
|
329
|
+
separate directories, sharing the same repo.
|
|
330
|
+
example: |
|
|
331
|
+
git worktree add ../hotfix hotfix-branch
|
|
332
|
+
cd ../hotfix
|
|
333
|
+
# work on hotfix without stashing your feature work
|
|
334
|
+
level: advanced
|
|
335
|
+
source: https://git-scm.com/docs/git-worktree
|
|
336
|
+
|
|
337
|
+
- id: git-008
|
|
338
|
+
topic: git
|
|
339
|
+
title: Use git diff --staged to see what you'll commit
|
|
340
|
+
body: >
|
|
341
|
+
`git diff` shows unstaged changes, but `git diff --staged` shows exactly
|
|
342
|
+
what will be included in the next commit.
|
|
343
|
+
example: |
|
|
344
|
+
git add src/main.py
|
|
345
|
+
git diff --staged # shows only staged changes
|
|
346
|
+
level: beginner
|
|
347
|
+
source: https://git-scm.com/docs/git-diff
|
|
348
|
+
|
|
349
|
+
- id: git-009
|
|
350
|
+
topic: git
|
|
351
|
+
title: Use git reflog to recover lost commits
|
|
352
|
+
body: >
|
|
353
|
+
The reflog records every time HEAD changes. Even after a hard reset or
|
|
354
|
+
deleted branch, you can find and restore lost commits.
|
|
355
|
+
example: |
|
|
356
|
+
git reflog
|
|
357
|
+
# Find the lost commit hash, then:
|
|
358
|
+
git checkout -b recovery abc1234
|
|
359
|
+
level: advanced
|
|
360
|
+
source: https://git-scm.com/docs/git-reflog
|
|
361
|
+
|
|
362
|
+
- id: git-010
|
|
363
|
+
topic: git
|
|
364
|
+
title: Partial staging with git add -p
|
|
365
|
+
body: >
|
|
366
|
+
`git add -p` lets you interactively select which hunks of a file to stage.
|
|
367
|
+
Great for splitting changes into logical commits.
|
|
368
|
+
example: |
|
|
369
|
+
git add -p src/main.py
|
|
370
|
+
# For each hunk, press: y (stage), n (skip), s (split)
|
|
371
|
+
level: intermediate
|
|
372
|
+
source: https://git-scm.com/docs/git-add
|
|
373
|
+
|
|
374
|
+
- id: git-011
|
|
375
|
+
topic: git
|
|
376
|
+
title: Use git blame to find who changed a line
|
|
377
|
+
body: >
|
|
378
|
+
`git blame` shows who last modified each line of a file and in which
|
|
379
|
+
commit. Use -L to narrow down to specific lines.
|
|
380
|
+
example: |
|
|
381
|
+
git blame src/main.py
|
|
382
|
+
# Blame specific lines
|
|
383
|
+
git blame -L 10,20 src/main.py
|
|
384
|
+
# Ignore whitespace changes
|
|
385
|
+
git blame -w src/main.py
|
|
386
|
+
level: beginner
|
|
387
|
+
source: https://git-scm.com/docs/git-blame
|
|
388
|
+
|
|
389
|
+
- id: git-012
|
|
390
|
+
topic: git
|
|
391
|
+
title: Set up git aliases for common commands
|
|
392
|
+
body: >
|
|
393
|
+
Git aliases save keystrokes for frequently used commands.
|
|
394
|
+
Define them in your global git config.
|
|
395
|
+
example: |
|
|
396
|
+
git config --global alias.co checkout
|
|
397
|
+
git config --global alias.br branch
|
|
398
|
+
git config --global alias.st status
|
|
399
|
+
git config --global alias.lg "log --oneline --graph --all"
|
|
400
|
+
# Now use: git co main, git lg
|
|
401
|
+
level: beginner
|
|
402
|
+
source: https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases
|
|
403
|
+
|
|
404
|
+
- id: git-013
|
|
405
|
+
topic: git
|
|
406
|
+
title: Use git switch and git restore
|
|
407
|
+
body: >
|
|
408
|
+
Modern Git split checkout into two commands. `git switch` changes branches,
|
|
409
|
+
`git restore` discards file changes. Less ambiguous than checkout.
|
|
410
|
+
example: |
|
|
411
|
+
# Switch branches (instead of git checkout main)
|
|
412
|
+
git switch main
|
|
413
|
+
git switch -c new-feature # create + switch
|
|
414
|
+
|
|
415
|
+
# Restore files (instead of git checkout -- file)
|
|
416
|
+
git restore src/main.py
|
|
417
|
+
git restore --staged src/main.py # unstage
|
|
418
|
+
level: intermediate
|
|
419
|
+
source: https://git-scm.com/docs/git-switch
|
|
420
|
+
|
|
421
|
+
- id: git-014
|
|
422
|
+
topic: git
|
|
423
|
+
title: Use git commit --fixup for clean history
|
|
424
|
+
body: >
|
|
425
|
+
`--fixup` creates a commit that marks itself as a fix for a previous
|
|
426
|
+
commit. Then `rebase --autosquash` merges them automatically.
|
|
427
|
+
example: |
|
|
428
|
+
# Fix something that should have been in abc1234
|
|
429
|
+
git commit --fixup abc1234
|
|
430
|
+
# Later, squash fixups automatically
|
|
431
|
+
git rebase -i --autosquash main
|
|
432
|
+
level: advanced
|
|
433
|
+
source: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---fixupamaborgrewordltcommitgt
|
|
434
|
+
|
|
435
|
+
- id: git-015
|
|
436
|
+
topic: git
|
|
437
|
+
title: Use git diff with word-level granularity
|
|
438
|
+
body: >
|
|
439
|
+
`--word-diff` shows changes at the word level instead of whole lines.
|
|
440
|
+
Makes it much easier to spot small changes in prose or config files.
|
|
441
|
+
example: |
|
|
442
|
+
git diff --word-diff
|
|
443
|
+
# Colored inline changes
|
|
444
|
+
git diff --word-diff=color
|
|
445
|
+
# Custom word regex (useful for code)
|
|
446
|
+
git diff --word-diff-regex='[a-zA-Z_]+|.'
|
|
447
|
+
level: intermediate
|
|
448
|
+
source: https://git-scm.com/docs/git-diff
|
|
449
|
+
|
|
450
|
+
# =============================================================================
|
|
451
|
+
# Docker tips
|
|
452
|
+
# =============================================================================
|
|
453
|
+
|
|
454
|
+
- id: docker-001
|
|
455
|
+
topic: docker
|
|
456
|
+
title: Use multi-stage builds for smaller images
|
|
457
|
+
body: >
|
|
458
|
+
Multi-stage builds let you use one stage for building and another for the
|
|
459
|
+
final image. This keeps your production image small and clean.
|
|
460
|
+
example: |
|
|
461
|
+
FROM node:20 AS builder
|
|
462
|
+
WORKDIR /app
|
|
463
|
+
COPY . .
|
|
464
|
+
RUN npm ci && npm run build
|
|
465
|
+
|
|
466
|
+
FROM node:20-slim
|
|
467
|
+
COPY --from=builder /app/dist ./dist
|
|
468
|
+
CMD ["node", "dist/index.js"]
|
|
469
|
+
level: intermediate
|
|
470
|
+
source: https://docs.docker.com/build/building/multi-stage/
|
|
471
|
+
|
|
472
|
+
- id: docker-002
|
|
473
|
+
topic: docker
|
|
474
|
+
title: Use .dockerignore to speed up builds
|
|
475
|
+
body: >
|
|
476
|
+
A .dockerignore file prevents unnecessary files from being sent to the
|
|
477
|
+
Docker daemon during builds, making them faster.
|
|
478
|
+
example: |
|
|
479
|
+
# .dockerignore
|
|
480
|
+
node_modules
|
|
481
|
+
.git
|
|
482
|
+
*.md
|
|
483
|
+
.env
|
|
484
|
+
level: beginner
|
|
485
|
+
source: https://docs.docker.com/build/building/context/#dockerignore-files
|
|
486
|
+
|
|
487
|
+
- id: docker-003
|
|
488
|
+
topic: docker
|
|
489
|
+
title: Use HEALTHCHECK in your Dockerfile
|
|
490
|
+
body: >
|
|
491
|
+
HEALTHCHECK tells Docker how to verify your container is still working.
|
|
492
|
+
Orchestrators use this to restart unhealthy containers.
|
|
493
|
+
example: |
|
|
494
|
+
HEALTHCHECK --interval=30s --timeout=3s \
|
|
495
|
+
CMD curl -f http://localhost:8080/health || exit 1
|
|
496
|
+
level: intermediate
|
|
497
|
+
source: https://docs.docker.com/reference/dockerfile/#healthcheck
|
|
498
|
+
|
|
499
|
+
- id: docker-004
|
|
500
|
+
topic: docker
|
|
501
|
+
title: Pin image versions in production
|
|
502
|
+
body: >
|
|
503
|
+
Always use specific image tags instead of `latest` in production.
|
|
504
|
+
This ensures reproducible builds and deployments.
|
|
505
|
+
example: |
|
|
506
|
+
# Bad
|
|
507
|
+
FROM python:latest
|
|
508
|
+
# Good
|
|
509
|
+
FROM python:3.12-slim@sha256:abc123...
|
|
510
|
+
level: beginner
|
|
511
|
+
source: https://docs.docker.com/build/building/best-practices/
|
|
512
|
+
|
|
513
|
+
- id: docker-005
|
|
514
|
+
topic: docker
|
|
515
|
+
title: Use COPY --chown to avoid extra RUN layers
|
|
516
|
+
body: >
|
|
517
|
+
Instead of COPY + RUN chown (which creates two layers), use the --chown
|
|
518
|
+
flag to set ownership in a single layer.
|
|
519
|
+
example: |
|
|
520
|
+
# Instead of:
|
|
521
|
+
COPY app/ /app/
|
|
522
|
+
RUN chown -R appuser:appuser /app
|
|
523
|
+
# Use:
|
|
524
|
+
COPY --chown=appuser:appuser app/ /app/
|
|
525
|
+
level: intermediate
|
|
526
|
+
source: https://docs.docker.com/reference/dockerfile/#copy
|
|
527
|
+
|
|
528
|
+
- id: docker-006
|
|
529
|
+
topic: docker
|
|
530
|
+
title: Use docker compose watch for live reload
|
|
531
|
+
body: >
|
|
532
|
+
Docker Compose Watch automatically syncs file changes and rebuilds containers
|
|
533
|
+
during development, without manual restarts.
|
|
534
|
+
example: |
|
|
535
|
+
# compose.yaml
|
|
536
|
+
services:
|
|
537
|
+
web:
|
|
538
|
+
build: .
|
|
539
|
+
develop:
|
|
540
|
+
watch:
|
|
541
|
+
- action: sync
|
|
542
|
+
path: ./src
|
|
543
|
+
target: /app/src
|
|
544
|
+
level: intermediate
|
|
545
|
+
source: https://docs.docker.com/compose/how-tos/file-watch/
|
|
546
|
+
|
|
547
|
+
- id: docker-007
|
|
548
|
+
topic: docker
|
|
549
|
+
title: Use docker system prune to free disk space
|
|
550
|
+
body: >
|
|
551
|
+
Docker can accumulate unused images, containers, and volumes over time.
|
|
552
|
+
`docker system prune` cleans up everything that's not in use.
|
|
553
|
+
example: |
|
|
554
|
+
# Remove unused data
|
|
555
|
+
docker system prune
|
|
556
|
+
# Also remove unused volumes
|
|
557
|
+
docker system prune --volumes
|
|
558
|
+
# See disk usage first
|
|
559
|
+
docker system df
|
|
560
|
+
level: beginner
|
|
561
|
+
source: https://docs.docker.com/reference/cli/docker/system/prune/
|
|
562
|
+
|
|
563
|
+
- id: docker-008
|
|
564
|
+
topic: docker
|
|
565
|
+
title: Order Dockerfile layers for better caching
|
|
566
|
+
body: >
|
|
567
|
+
Docker caches each layer. Put rarely-changing instructions (like installing
|
|
568
|
+
dependencies) before frequently-changing ones (like copying source code).
|
|
569
|
+
example: |
|
|
570
|
+
FROM python:3.12-slim
|
|
571
|
+
WORKDIR /app
|
|
572
|
+
# Dependencies change rarely - cached
|
|
573
|
+
COPY requirements.txt .
|
|
574
|
+
RUN pip install -r requirements.txt
|
|
575
|
+
# Source changes often - not cached
|
|
576
|
+
COPY . .
|
|
577
|
+
level: beginner
|
|
578
|
+
source: https://docs.docker.com/build/cache/
|
|
579
|
+
|
|
580
|
+
- id: docker-009
|
|
581
|
+
topic: docker
|
|
582
|
+
title: Use build arguments for flexible images
|
|
583
|
+
body: >
|
|
584
|
+
ARG lets you pass build-time variables to your Dockerfile. Useful for
|
|
585
|
+
version numbers, environment selection, and conditional logic.
|
|
586
|
+
example: |
|
|
587
|
+
ARG PYTHON_VERSION=3.12
|
|
588
|
+
FROM python:${PYTHON_VERSION}-slim
|
|
589
|
+
ARG ENV=production
|
|
590
|
+
RUN if [ "$ENV" = "development" ]; then pip install debugpy; fi
|
|
591
|
+
level: intermediate
|
|
592
|
+
source: https://docs.docker.com/reference/dockerfile/#arg
|
|
593
|
+
|
|
594
|
+
- id: docker-010
|
|
595
|
+
topic: docker
|
|
596
|
+
title: Use docker exec to debug running containers
|
|
597
|
+
body: >
|
|
598
|
+
`docker exec` runs a command inside a running container. Essential for
|
|
599
|
+
debugging and inspecting container state.
|
|
600
|
+
example: |
|
|
601
|
+
# Open a shell in a running container
|
|
602
|
+
docker exec -it mycontainer /bin/sh
|
|
603
|
+
# Check environment variables
|
|
604
|
+
docker exec mycontainer env
|
|
605
|
+
# View logs inside container
|
|
606
|
+
docker exec mycontainer cat /var/log/app.log
|
|
607
|
+
level: beginner
|
|
608
|
+
source: https://docs.docker.com/reference/cli/docker/container/exec/
|
|
609
|
+
|
|
610
|
+
- id: docker-011
|
|
611
|
+
topic: docker
|
|
612
|
+
title: Use docker compose profiles for environments
|
|
613
|
+
body: >
|
|
614
|
+
Profiles let you define services that only start when explicitly requested.
|
|
615
|
+
Useful for debug tools, monitoring, or test databases.
|
|
616
|
+
example: |
|
|
617
|
+
# compose.yaml
|
|
618
|
+
services:
|
|
619
|
+
app:
|
|
620
|
+
build: .
|
|
621
|
+
debug:
|
|
622
|
+
image: busybox
|
|
623
|
+
profiles: ["debug"]
|
|
624
|
+
mailhog:
|
|
625
|
+
image: mailhog/mailhog
|
|
626
|
+
profiles: ["debug"]
|
|
627
|
+
|
|
628
|
+
# docker compose --profile debug up
|
|
629
|
+
level: intermediate
|
|
630
|
+
source: https://docs.docker.com/compose/how-tos/profiles/
|
|
631
|
+
|
|
632
|
+
- id: docker-012
|
|
633
|
+
topic: docker
|
|
634
|
+
title: Run as non-root user for security
|
|
635
|
+
body: >
|
|
636
|
+
Containers run as root by default. Create a non-root user to limit
|
|
637
|
+
the blast radius if your container is compromised.
|
|
638
|
+
example: |
|
|
639
|
+
FROM python:3.12-slim
|
|
640
|
+
RUN groupadd -r app && useradd -r -g app app
|
|
641
|
+
WORKDIR /app
|
|
642
|
+
COPY --chown=app:app . .
|
|
643
|
+
USER app
|
|
644
|
+
CMD ["python", "main.py"]
|
|
645
|
+
level: intermediate
|
|
646
|
+
source: https://docs.docker.com/build/building/best-practices/#user
|
|
647
|
+
|
|
648
|
+
- id: docker-013
|
|
649
|
+
topic: docker
|
|
650
|
+
title: Use docker logs to troubleshoot containers
|
|
651
|
+
body: >
|
|
652
|
+
`docker logs` shows stdout/stderr from a container. Use --follow to
|
|
653
|
+
stream in real time and --since to filter by time.
|
|
654
|
+
example: |
|
|
655
|
+
# View all logs
|
|
656
|
+
docker logs mycontainer
|
|
657
|
+
# Stream logs in real time
|
|
658
|
+
docker logs -f mycontainer
|
|
659
|
+
# Last 100 lines from past hour
|
|
660
|
+
docker logs --tail 100 --since 1h mycontainer
|
|
661
|
+
level: beginner
|
|
662
|
+
source: https://docs.docker.com/reference/cli/docker/container/logs/
|
|
663
|
+
|
|
664
|
+
- id: docker-014
|
|
665
|
+
topic: docker
|
|
666
|
+
title: Use tmpfs mounts for sensitive data
|
|
667
|
+
body: >
|
|
668
|
+
tmpfs mounts exist only in memory and are never written to disk.
|
|
669
|
+
Use them for secrets, temp files, or build caches.
|
|
670
|
+
example: |
|
|
671
|
+
# CLI
|
|
672
|
+
docker run --tmpfs /tmp:rw,noexec,nosuid myimage
|
|
673
|
+
|
|
674
|
+
# compose.yaml
|
|
675
|
+
services:
|
|
676
|
+
app:
|
|
677
|
+
tmpfs:
|
|
678
|
+
- /tmp
|
|
679
|
+
- /run
|
|
680
|
+
level: advanced
|
|
681
|
+
source: https://docs.docker.com/engine/storage/tmpfs/
|
|
682
|
+
|
|
683
|
+
- id: docker-015
|
|
684
|
+
topic: docker
|
|
685
|
+
title: Use docker network for container communication
|
|
686
|
+
body: >
|
|
687
|
+
Containers on the same Docker network can reach each other by service name.
|
|
688
|
+
No need to publish ports for inter-container communication.
|
|
689
|
+
example: |
|
|
690
|
+
# compose.yaml - app reaches db at "db:5432"
|
|
691
|
+
services:
|
|
692
|
+
app:
|
|
693
|
+
build: .
|
|
694
|
+
environment:
|
|
695
|
+
DATABASE_URL: postgresql://user:pass@db:5432/mydb
|
|
696
|
+
db:
|
|
697
|
+
image: postgres:16
|
|
698
|
+
level: beginner
|
|
699
|
+
source: https://docs.docker.com/engine/network/
|
|
700
|
+
|
|
701
|
+
# =============================================================================
|
|
702
|
+
# SQL tips
|
|
703
|
+
# =============================================================================
|
|
704
|
+
|
|
705
|
+
- id: sql-001
|
|
706
|
+
topic: sql
|
|
707
|
+
title: Use COALESCE to handle NULL values
|
|
708
|
+
body: >
|
|
709
|
+
COALESCE returns the first non-NULL value from a list of arguments.
|
|
710
|
+
Great for providing default values in queries.
|
|
711
|
+
example: |
|
|
712
|
+
SELECT name, COALESCE(nickname, name) AS display_name
|
|
713
|
+
FROM users;
|
|
714
|
+
level: beginner
|
|
715
|
+
source: https://www.postgresql.org/docs/current/functions-conditional.html
|
|
716
|
+
|
|
717
|
+
- id: sql-002
|
|
718
|
+
topic: sql
|
|
719
|
+
title: Use CTEs for readable complex queries
|
|
720
|
+
body: >
|
|
721
|
+
Common Table Expressions (WITH clauses) let you break complex queries
|
|
722
|
+
into named, readable steps.
|
|
723
|
+
example: |
|
|
724
|
+
WITH active_users AS (
|
|
725
|
+
SELECT * FROM users WHERE last_login > NOW() - INTERVAL '30 days'
|
|
726
|
+
)
|
|
727
|
+
SELECT department, COUNT(*) FROM active_users
|
|
728
|
+
GROUP BY department;
|
|
729
|
+
level: intermediate
|
|
730
|
+
source: https://www.postgresql.org/docs/current/queries-with.html
|
|
731
|
+
|
|
732
|
+
- id: sql-003
|
|
733
|
+
topic: sql
|
|
734
|
+
title: Use EXPLAIN ANALYZE to debug slow queries
|
|
735
|
+
body: >
|
|
736
|
+
EXPLAIN ANALYZE runs the query and shows the actual execution plan
|
|
737
|
+
with timing information. Essential for performance tuning.
|
|
738
|
+
example: |
|
|
739
|
+
EXPLAIN ANALYZE
|
|
740
|
+
SELECT * FROM orders
|
|
741
|
+
WHERE customer_id = 42
|
|
742
|
+
ORDER BY created_at DESC;
|
|
743
|
+
level: advanced
|
|
744
|
+
source: https://www.postgresql.org/docs/current/using-explain.html
|
|
745
|
+
|
|
746
|
+
- id: sql-004
|
|
747
|
+
topic: sql
|
|
748
|
+
title: Use EXISTS instead of IN for subqueries
|
|
749
|
+
body: >
|
|
750
|
+
EXISTS can be faster than IN for correlated subqueries because it
|
|
751
|
+
stops scanning once a match is found.
|
|
752
|
+
example: |
|
|
753
|
+
-- Instead of: WHERE id IN (SELECT user_id FROM orders)
|
|
754
|
+
SELECT * FROM users u
|
|
755
|
+
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
|
|
756
|
+
level: intermediate
|
|
757
|
+
source: https://www.postgresql.org/docs/current/functions-subquery.html
|
|
758
|
+
|
|
759
|
+
- id: sql-005
|
|
760
|
+
topic: sql
|
|
761
|
+
title: Use window functions for running totals
|
|
762
|
+
body: >
|
|
763
|
+
Window functions perform calculations across rows related to the current
|
|
764
|
+
row, without collapsing them into groups like GROUP BY.
|
|
765
|
+
example: |
|
|
766
|
+
SELECT
|
|
767
|
+
date,
|
|
768
|
+
revenue,
|
|
769
|
+
SUM(revenue) OVER (ORDER BY date) AS running_total,
|
|
770
|
+
AVG(revenue) OVER (ORDER BY date ROWS 6 PRECEDING) AS moving_avg_7d
|
|
771
|
+
FROM daily_sales;
|
|
772
|
+
level: advanced
|
|
773
|
+
source: https://www.postgresql.org/docs/current/tutorial-window.html
|
|
774
|
+
|
|
775
|
+
- id: sql-006
|
|
776
|
+
topic: sql
|
|
777
|
+
title: Use DISTINCT ON for top-per-group queries
|
|
778
|
+
body: >
|
|
779
|
+
PostgreSQL's DISTINCT ON returns the first row for each unique value
|
|
780
|
+
of the specified columns. Simpler than window function alternatives.
|
|
781
|
+
example: |
|
|
782
|
+
-- Get the most recent order per customer
|
|
783
|
+
SELECT DISTINCT ON (customer_id)
|
|
784
|
+
customer_id, order_id, created_at, total
|
|
785
|
+
FROM orders
|
|
786
|
+
ORDER BY customer_id, created_at DESC;
|
|
787
|
+
level: intermediate
|
|
788
|
+
source: https://www.postgresql.org/docs/current/sql-select.html#SQL-DISTINCT
|
|
789
|
+
|
|
790
|
+
- id: sql-007
|
|
791
|
+
topic: sql
|
|
792
|
+
title: Use partial indexes for filtered queries
|
|
793
|
+
body: >
|
|
794
|
+
A partial index only indexes rows matching a WHERE condition. Smaller
|
|
795
|
+
and faster than a full index for queries that always filter the same way.
|
|
796
|
+
example: |
|
|
797
|
+
-- Only index active users (most queries filter on active)
|
|
798
|
+
CREATE INDEX idx_active_users ON users (email)
|
|
799
|
+
WHERE is_active = true;
|
|
800
|
+
level: advanced
|
|
801
|
+
source: https://www.postgresql.org/docs/current/indexes-partial.html
|
|
802
|
+
|
|
803
|
+
- id: sql-008
|
|
804
|
+
topic: sql
|
|
805
|
+
title: Use LATERAL joins for row-dependent subqueries
|
|
806
|
+
body: >
|
|
807
|
+
LATERAL lets a subquery reference columns from preceding FROM items.
|
|
808
|
+
Think of it as a SQL foreach loop.
|
|
809
|
+
example: |
|
|
810
|
+
SELECT u.name, recent.title
|
|
811
|
+
FROM users u
|
|
812
|
+
CROSS JOIN LATERAL (
|
|
813
|
+
SELECT title FROM posts
|
|
814
|
+
WHERE posts.user_id = u.id
|
|
815
|
+
ORDER BY created_at DESC LIMIT 3
|
|
816
|
+
) AS recent;
|
|
817
|
+
level: advanced
|
|
818
|
+
source: https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-LATERAL
|
|
819
|
+
|
|
820
|
+
- id: sql-009
|
|
821
|
+
topic: sql
|
|
822
|
+
title: Use RETURNING to get affected rows
|
|
823
|
+
body: >
|
|
824
|
+
The RETURNING clause returns data from rows modified by INSERT, UPDATE,
|
|
825
|
+
or DELETE, saving you an extra SELECT query.
|
|
826
|
+
example: |
|
|
827
|
+
INSERT INTO users (name, email)
|
|
828
|
+
VALUES ('Alice', 'alice@example.com')
|
|
829
|
+
RETURNING id, created_at;
|
|
830
|
+
|
|
831
|
+
UPDATE orders SET status = 'shipped'
|
|
832
|
+
WHERE status = 'pending'
|
|
833
|
+
RETURNING id, customer_id;
|
|
834
|
+
level: intermediate
|
|
835
|
+
source: https://www.postgresql.org/docs/current/dml-returning.html
|
|
836
|
+
|
|
837
|
+
- id: sql-010
|
|
838
|
+
topic: sql
|
|
839
|
+
title: Use CASE for conditional logic in queries
|
|
840
|
+
body: >
|
|
841
|
+
CASE expressions let you add if/else logic directly in SQL queries.
|
|
842
|
+
Works in SELECT, WHERE, ORDER BY, and aggregate functions.
|
|
843
|
+
example: |
|
|
844
|
+
SELECT name,
|
|
845
|
+
CASE
|
|
846
|
+
WHEN age < 18 THEN 'minor'
|
|
847
|
+
WHEN age < 65 THEN 'adult'
|
|
848
|
+
ELSE 'senior'
|
|
849
|
+
END AS age_group,
|
|
850
|
+
COUNT(*) FILTER (WHERE status = 'active') AS active_count
|
|
851
|
+
FROM users
|
|
852
|
+
GROUP BY name, age;
|
|
853
|
+
level: beginner
|
|
854
|
+
source: https://www.postgresql.org/docs/current/functions-conditional.html
|
|
855
|
+
|
|
856
|
+
- id: sql-011
|
|
857
|
+
topic: sql
|
|
858
|
+
title: Use UPSERT to insert or update in one statement
|
|
859
|
+
body: >
|
|
860
|
+
INSERT ... ON CONFLICT handles the common "insert if new, update if exists"
|
|
861
|
+
pattern in a single atomic statement.
|
|
862
|
+
example: |
|
|
863
|
+
INSERT INTO counters (page, hits)
|
|
864
|
+
VALUES ('home', 1)
|
|
865
|
+
ON CONFLICT (page)
|
|
866
|
+
DO UPDATE SET hits = counters.hits + 1;
|
|
867
|
+
level: intermediate
|
|
868
|
+
source: https://www.postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT
|
|
869
|
+
|
|
870
|
+
- id: sql-012
|
|
871
|
+
topic: sql
|
|
872
|
+
title: Use generate_series for test data
|
|
873
|
+
body: >
|
|
874
|
+
generate_series() creates rows on the fly. Perfect for generating test data,
|
|
875
|
+
filling date gaps, or creating sequences.
|
|
876
|
+
example: |
|
|
877
|
+
-- Generate dates for the past 30 days
|
|
878
|
+
SELECT date::date
|
|
879
|
+
FROM generate_series(
|
|
880
|
+
NOW() - INTERVAL '30 days', NOW(), '1 day'
|
|
881
|
+
) AS date;
|
|
882
|
+
|
|
883
|
+
-- Generate 1000 test users
|
|
884
|
+
INSERT INTO users (name, email)
|
|
885
|
+
SELECT 'user_' || n, 'user' || n || '@test.com'
|
|
886
|
+
FROM generate_series(1, 1000) AS n;
|
|
887
|
+
level: intermediate
|
|
888
|
+
source: https://www.postgresql.org/docs/current/functions-srf.html
|
|
889
|
+
|
|
890
|
+
- id: sql-013
|
|
891
|
+
topic: sql
|
|
892
|
+
title: Use jsonb for flexible schema data
|
|
893
|
+
body: >
|
|
894
|
+
PostgreSQL's jsonb stores JSON in a binary format with full indexing
|
|
895
|
+
support. Perfect for semi-structured data alongside regular columns.
|
|
896
|
+
example: |
|
|
897
|
+
CREATE TABLE events (
|
|
898
|
+
id serial PRIMARY KEY,
|
|
899
|
+
type text NOT NULL,
|
|
900
|
+
data jsonb NOT NULL
|
|
901
|
+
);
|
|
902
|
+
|
|
903
|
+
-- Query nested JSON
|
|
904
|
+
SELECT data->>'email' FROM events
|
|
905
|
+
WHERE data @> '{"role": "admin"}';
|
|
906
|
+
|
|
907
|
+
-- Index for fast lookups
|
|
908
|
+
CREATE INDEX idx_events_data ON events USING gin (data);
|
|
909
|
+
level: advanced
|
|
910
|
+
source: https://www.postgresql.org/docs/current/datatype-json.html
|
|
911
|
+
|
|
912
|
+
- id: sql-014
|
|
913
|
+
topic: sql
|
|
914
|
+
title: Use transactions to group related changes
|
|
915
|
+
body: >
|
|
916
|
+
Wrap related INSERT/UPDATE/DELETE statements in a transaction so they
|
|
917
|
+
either all succeed or all roll back together.
|
|
918
|
+
example: |
|
|
919
|
+
BEGIN;
|
|
920
|
+
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
|
|
921
|
+
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
|
|
922
|
+
COMMIT;
|
|
923
|
+
-- If anything fails, both changes are rolled back
|
|
924
|
+
level: beginner
|
|
925
|
+
source: https://www.postgresql.org/docs/current/tutorial-transactions.html
|
|
926
|
+
|
|
927
|
+
- id: sql-015
|
|
928
|
+
topic: sql
|
|
929
|
+
title: Use string_agg to concatenate grouped rows
|
|
930
|
+
body: >
|
|
931
|
+
string_agg() combines multiple row values into a single delimited string
|
|
932
|
+
within a GROUP BY. Useful for comma-separated lists.
|
|
933
|
+
example: |
|
|
934
|
+
SELECT department,
|
|
935
|
+
string_agg(name, ', ' ORDER BY name) AS members
|
|
936
|
+
FROM employees
|
|
937
|
+
GROUP BY department;
|
|
938
|
+
-- Result: "Engineering" | "Alice, Bob, Charlie"
|
|
939
|
+
level: beginner
|
|
940
|
+
source: https://www.postgresql.org/docs/current/functions-aggregate.html
|
|
941
|
+
|
|
942
|
+
# =============================================================================
|
|
943
|
+
# Linux tips
|
|
944
|
+
# =============================================================================
|
|
945
|
+
|
|
946
|
+
- id: linux-001
|
|
947
|
+
topic: linux
|
|
948
|
+
title: Use ctrl+r for reverse history search
|
|
949
|
+
body: >
|
|
950
|
+
Press ctrl+r in your terminal to search through your command history.
|
|
951
|
+
Keep pressing ctrl+r to cycle through matches.
|
|
952
|
+
example: |
|
|
953
|
+
# Press ctrl+r, then type 'docker'
|
|
954
|
+
# It will find your last command containing 'docker'
|
|
955
|
+
(reverse-i-search)`docker': docker compose up -d
|
|
956
|
+
level: beginner
|
|
957
|
+
source: https://www.gnu.org/software/bash/manual/bash.html
|
|
958
|
+
|
|
959
|
+
- id: linux-002
|
|
960
|
+
topic: linux
|
|
961
|
+
title: Use xargs to pass stdin as arguments
|
|
962
|
+
body: >
|
|
963
|
+
xargs reads items from stdin and executes a command with those items
|
|
964
|
+
as arguments. Combine with find or grep for powerful pipelines.
|
|
965
|
+
example: |
|
|
966
|
+
# Delete all .pyc files
|
|
967
|
+
find . -name "*.pyc" | xargs rm
|
|
968
|
+
# Run tests for changed files
|
|
969
|
+
git diff --name-only | grep test | xargs pytest
|
|
970
|
+
level: intermediate
|
|
971
|
+
source: https://man7.org/linux/man-pages/man1/xargs.1.html
|
|
972
|
+
|
|
973
|
+
- id: linux-003
|
|
974
|
+
topic: linux
|
|
975
|
+
title: Use tee to write to file and stdout
|
|
976
|
+
body: >
|
|
977
|
+
The tee command reads from stdin and writes to both stdout and a file.
|
|
978
|
+
Useful for logging command output while still seeing it.
|
|
979
|
+
example: |
|
|
980
|
+
# Save build output to file while watching it
|
|
981
|
+
make build 2>&1 | tee build.log
|
|
982
|
+
level: intermediate
|
|
983
|
+
source: https://man7.org/linux/man-pages/man1/tee.1.html
|
|
984
|
+
|
|
985
|
+
- id: linux-004
|
|
986
|
+
topic: linux
|
|
987
|
+
title: Use watch to repeat a command
|
|
988
|
+
body: >
|
|
989
|
+
The watch command runs a command repeatedly and displays the output.
|
|
990
|
+
Great for monitoring changing values.
|
|
991
|
+
example: |
|
|
992
|
+
# Watch pod status every 2 seconds
|
|
993
|
+
watch -n 2 kubectl get pods
|
|
994
|
+
# Watch disk usage
|
|
995
|
+
watch df -h
|
|
996
|
+
level: beginner
|
|
997
|
+
source: https://man7.org/linux/man-pages/man1/watch.1.html
|
|
998
|
+
|
|
999
|
+
- id: linux-005
|
|
1000
|
+
topic: linux
|
|
1001
|
+
title: Use curly braces for quick file operations
|
|
1002
|
+
body: >
|
|
1003
|
+
Brace expansion generates multiple strings from a pattern. Useful for
|
|
1004
|
+
creating backups, renaming, or operating on similar paths.
|
|
1005
|
+
example: |
|
|
1006
|
+
# Create a backup
|
|
1007
|
+
cp config.yaml{,.bak}
|
|
1008
|
+
# Create multiple directories
|
|
1009
|
+
mkdir -p project/{src,tests,docs}
|
|
1010
|
+
# Rename extension
|
|
1011
|
+
mv app.{js,ts}
|
|
1012
|
+
level: beginner
|
|
1013
|
+
source: https://www.gnu.org/software/bash/manual/bash.html#Brace-Expansion
|
|
1014
|
+
|
|
1015
|
+
- id: linux-006
|
|
1016
|
+
topic: linux
|
|
1017
|
+
title: Use process substitution for diff-like comparisons
|
|
1018
|
+
body: >
|
|
1019
|
+
Process substitution <() lets you use command output as if it were a file.
|
|
1020
|
+
Perfect for comparing outputs of two commands.
|
|
1021
|
+
example: |
|
|
1022
|
+
# Compare two branches' file lists
|
|
1023
|
+
diff <(git ls-tree -r --name-only main) <(git ls-tree -r --name-only dev)
|
|
1024
|
+
# Compare sorted outputs
|
|
1025
|
+
diff <(sort file1.txt) <(sort file2.txt)
|
|
1026
|
+
level: advanced
|
|
1027
|
+
source: https://www.gnu.org/software/bash/manual/bash.html#Process-Substitution
|
|
1028
|
+
|
|
1029
|
+
- id: linux-007
|
|
1030
|
+
topic: linux
|
|
1031
|
+
title: Use trap to clean up on script exit
|
|
1032
|
+
body: >
|
|
1033
|
+
The trap command runs a function when your script exits, whether
|
|
1034
|
+
normally or due to an error. Essential for cleaning up temp files.
|
|
1035
|
+
example: |
|
|
1036
|
+
#!/bin/bash
|
|
1037
|
+
tmpfile=$(mktemp)
|
|
1038
|
+
trap "rm -f $tmpfile" EXIT
|
|
1039
|
+
|
|
1040
|
+
# Script runs... temp file is always cleaned up
|
|
1041
|
+
curl -o "$tmpfile" https://example.com/data
|
|
1042
|
+
process "$tmpfile"
|
|
1043
|
+
level: intermediate
|
|
1044
|
+
source: https://www.gnu.org/software/bash/manual/bash.html#Bourne-Shell-Builtins
|
|
1045
|
+
|
|
1046
|
+
- id: linux-008
|
|
1047
|
+
topic: linux
|
|
1048
|
+
title: Use ss instead of netstat for network info
|
|
1049
|
+
body: >
|
|
1050
|
+
ss (socket statistics) is faster and more informative than netstat.
|
|
1051
|
+
It's the modern replacement on Linux systems.
|
|
1052
|
+
example: |
|
|
1053
|
+
# List listening TCP ports
|
|
1054
|
+
ss -tlnp
|
|
1055
|
+
# Show established connections
|
|
1056
|
+
ss -tn state established
|
|
1057
|
+
# Find what's using port 8080
|
|
1058
|
+
ss -tlnp | grep 8080
|
|
1059
|
+
level: intermediate
|
|
1060
|
+
source: https://man7.org/linux/man-pages/man8/ss.8.html
|
|
1061
|
+
|
|
1062
|
+
- id: linux-009
|
|
1063
|
+
topic: linux
|
|
1064
|
+
title: Use set -euo pipefail in bash scripts
|
|
1065
|
+
body: >
|
|
1066
|
+
This combination makes bash scripts fail fast on errors instead of
|
|
1067
|
+
silently continuing. Essential for reliable scripts.
|
|
1068
|
+
example: |
|
|
1069
|
+
#!/bin/bash
|
|
1070
|
+
set -euo pipefail
|
|
1071
|
+
# -e: exit on error
|
|
1072
|
+
# -u: error on undefined variables
|
|
1073
|
+
# -o pipefail: catch errors in piped commands
|
|
1074
|
+
level: beginner
|
|
1075
|
+
source: https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin
|
|
1076
|
+
|
|
1077
|
+
- id: linux-010
|
|
1078
|
+
topic: linux
|
|
1079
|
+
title: Use jq to parse JSON on the command line
|
|
1080
|
+
body: >
|
|
1081
|
+
jq is a lightweight command-line JSON processor. It can filter, transform,
|
|
1082
|
+
and format JSON data from APIs and files.
|
|
1083
|
+
example: |
|
|
1084
|
+
# Pretty-print JSON
|
|
1085
|
+
curl -s api.example.com/users | jq '.'
|
|
1086
|
+
# Extract specific fields
|
|
1087
|
+
cat data.json | jq '.users[] | {name, email}'
|
|
1088
|
+
# Filter by condition
|
|
1089
|
+
jq '.items[] | select(.price > 100)' catalog.json
|
|
1090
|
+
level: intermediate
|
|
1091
|
+
source: https://jqlang.github.io/jq/manual/
|
|
1092
|
+
|
|
1093
|
+
- id: linux-011
|
|
1094
|
+
topic: linux
|
|
1095
|
+
title: Use rsync instead of cp for large copies
|
|
1096
|
+
body: >
|
|
1097
|
+
rsync only transfers changed files and can resume interrupted transfers.
|
|
1098
|
+
Far better than cp for large directories or remote copies.
|
|
1099
|
+
example: |
|
|
1100
|
+
# Copy directory (only changed files)
|
|
1101
|
+
rsync -avz src/ dest/
|
|
1102
|
+
# Copy to remote with progress
|
|
1103
|
+
rsync -avz --progress ./data user@server:/backup/
|
|
1104
|
+
# Dry run to preview changes
|
|
1105
|
+
rsync -avzn src/ dest/
|
|
1106
|
+
level: intermediate
|
|
1107
|
+
source: https://man7.org/linux/man-pages/man1/rsync.1.html
|
|
1108
|
+
|
|
1109
|
+
- id: linux-012
|
|
1110
|
+
topic: linux
|
|
1111
|
+
title: Use !! to repeat the last command
|
|
1112
|
+
body: >
|
|
1113
|
+
`!!` expands to your last command. Most commonly used with sudo when
|
|
1114
|
+
you forgot to run something as root.
|
|
1115
|
+
example: |
|
|
1116
|
+
apt install nginx
|
|
1117
|
+
# Permission denied
|
|
1118
|
+
sudo !!
|
|
1119
|
+
# Expands to: sudo apt install nginx
|
|
1120
|
+
level: beginner
|
|
1121
|
+
source: https://www.gnu.org/software/bash/manual/bash.html#Event-Designators
|
|
1122
|
+
|
|
1123
|
+
- id: linux-013
|
|
1124
|
+
topic: linux
|
|
1125
|
+
title: Use column to format command output as a table
|
|
1126
|
+
body: >
|
|
1127
|
+
The column command formats text into aligned columns.
|
|
1128
|
+
Pipe any delimited output through it for instant readability.
|
|
1129
|
+
example: |
|
|
1130
|
+
# Format CSV as table
|
|
1131
|
+
cat data.csv | column -t -s ','
|
|
1132
|
+
# Format mount output
|
|
1133
|
+
mount | column -t
|
|
1134
|
+
level: beginner
|
|
1135
|
+
source: https://man7.org/linux/man-pages/man1/column.1.html
|
|
1136
|
+
|
|
1137
|
+
- id: linux-014
|
|
1138
|
+
topic: linux
|
|
1139
|
+
title: Use nohup to keep commands running after logout
|
|
1140
|
+
body: >
|
|
1141
|
+
nohup prevents a command from being killed when you close the terminal.
|
|
1142
|
+
Output is saved to nohup.out by default.
|
|
1143
|
+
example: |
|
|
1144
|
+
# Run a long process that survives logout
|
|
1145
|
+
nohup python train_model.py &
|
|
1146
|
+
# Check output later
|
|
1147
|
+
tail -f nohup.out
|
|
1148
|
+
level: intermediate
|
|
1149
|
+
source: https://man7.org/linux/man-pages/man1/nohup.1.html
|
|
1150
|
+
|
|
1151
|
+
- id: linux-015
|
|
1152
|
+
topic: linux
|
|
1153
|
+
title: Use lsof to find what's using a file or port
|
|
1154
|
+
body: >
|
|
1155
|
+
lsof (list open files) shows which processes have a file or port open.
|
|
1156
|
+
Essential for debugging "address already in use" errors.
|
|
1157
|
+
example: |
|
|
1158
|
+
# Find what's using port 8080
|
|
1159
|
+
lsof -i :8080
|
|
1160
|
+
# Find who has a file open
|
|
1161
|
+
lsof /var/log/syslog
|
|
1162
|
+
# List all files opened by a process
|
|
1163
|
+
lsof -p 1234
|
|
1164
|
+
level: intermediate
|
|
1165
|
+
source: https://man7.org/linux/man-pages/man8/lsof.8.html
|
|
1166
|
+
|
|
1167
|
+
# =============================================================================
|
|
1168
|
+
# Kubernetes tips
|
|
1169
|
+
# =============================================================================
|
|
1170
|
+
|
|
1171
|
+
- id: k8s-001
|
|
1172
|
+
topic: kubernetes
|
|
1173
|
+
title: Use kubectl explain to explore resource specs
|
|
1174
|
+
body: >
|
|
1175
|
+
`kubectl explain` shows the schema for any Kubernetes resource field.
|
|
1176
|
+
No need to look up docs — it's built into the CLI.
|
|
1177
|
+
example: |
|
|
1178
|
+
kubectl explain pod.spec.containers
|
|
1179
|
+
kubectl explain deployment.spec.strategy
|
|
1180
|
+
# Recursive view
|
|
1181
|
+
kubectl explain pod.spec --recursive
|
|
1182
|
+
level: beginner
|
|
1183
|
+
source: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_explain/
|
|
1184
|
+
|
|
1185
|
+
- id: k8s-002
|
|
1186
|
+
topic: kubernetes
|
|
1187
|
+
title: Use labels and selectors for everything
|
|
1188
|
+
body: >
|
|
1189
|
+
Labels are key-value pairs attached to resources. Selectors let you query
|
|
1190
|
+
and operate on groups of resources matching specific labels.
|
|
1191
|
+
example: |
|
|
1192
|
+
# Add labels
|
|
1193
|
+
kubectl label pods my-pod env=staging
|
|
1194
|
+
|
|
1195
|
+
# Select by label
|
|
1196
|
+
kubectl get pods -l app=web,env=prod
|
|
1197
|
+
kubectl delete pods -l job=cleanup
|
|
1198
|
+
level: beginner
|
|
1199
|
+
source: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
|
1200
|
+
|
|
1201
|
+
- id: k8s-003
|
|
1202
|
+
topic: kubernetes
|
|
1203
|
+
title: Use kubectl port-forward for local debugging
|
|
1204
|
+
body: >
|
|
1205
|
+
port-forward tunnels a local port to a pod or service. Access internal
|
|
1206
|
+
services without exposing them externally.
|
|
1207
|
+
example: |
|
|
1208
|
+
# Forward local 8080 to pod's 80
|
|
1209
|
+
kubectl port-forward pod/my-pod 8080:80
|
|
1210
|
+
# Forward to a service
|
|
1211
|
+
kubectl port-forward svc/my-service 5432:5432
|
|
1212
|
+
# Now connect at localhost:8080 or localhost:5432
|
|
1213
|
+
level: beginner
|
|
1214
|
+
source: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_port-forward/
|
|
1215
|
+
|
|
1216
|
+
- id: k8s-004
|
|
1217
|
+
topic: kubernetes
|
|
1218
|
+
title: Use resource requests and limits
|
|
1219
|
+
body: >
|
|
1220
|
+
Requests guarantee minimum resources for your pod. Limits cap the maximum.
|
|
1221
|
+
Without them, a single pod can starve the entire node.
|
|
1222
|
+
example: |
|
|
1223
|
+
resources:
|
|
1224
|
+
requests:
|
|
1225
|
+
memory: "128Mi"
|
|
1226
|
+
cpu: "250m"
|
|
1227
|
+
limits:
|
|
1228
|
+
memory: "256Mi"
|
|
1229
|
+
cpu: "500m"
|
|
1230
|
+
level: intermediate
|
|
1231
|
+
source: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
|
|
1232
|
+
|
|
1233
|
+
- id: k8s-005
|
|
1234
|
+
topic: kubernetes
|
|
1235
|
+
title: Use kubectl get with custom columns
|
|
1236
|
+
body: >
|
|
1237
|
+
Custom columns let you extract exactly the fields you need from resources.
|
|
1238
|
+
Much more flexible than the default table output.
|
|
1239
|
+
example: |
|
|
1240
|
+
kubectl get pods -o custom-columns=\
|
|
1241
|
+
NAME:.metadata.name,\
|
|
1242
|
+
STATUS:.status.phase,\
|
|
1243
|
+
NODE:.spec.nodeName,\
|
|
1244
|
+
IP:.status.podIP
|
|
1245
|
+
level: intermediate
|
|
1246
|
+
source: https://kubernetes.io/docs/reference/kubectl/
|
|
1247
|
+
|
|
1248
|
+
- id: k8s-006
|
|
1249
|
+
topic: kubernetes
|
|
1250
|
+
title: Use ConfigMaps for non-secret configuration
|
|
1251
|
+
body: >
|
|
1252
|
+
ConfigMaps decouple configuration from container images. Mount them as
|
|
1253
|
+
files or inject as environment variables.
|
|
1254
|
+
example: |
|
|
1255
|
+
# Create from file
|
|
1256
|
+
kubectl create configmap app-config --from-file=config.yaml
|
|
1257
|
+
|
|
1258
|
+
# Use in pod
|
|
1259
|
+
envFrom:
|
|
1260
|
+
- configMapRef:
|
|
1261
|
+
name: app-config
|
|
1262
|
+
# Or mount as volume
|
|
1263
|
+
volumes:
|
|
1264
|
+
- name: config
|
|
1265
|
+
configMap:
|
|
1266
|
+
name: app-config
|
|
1267
|
+
level: intermediate
|
|
1268
|
+
source: https://kubernetes.io/docs/concepts/configuration/configmap/
|
|
1269
|
+
|
|
1270
|
+
- id: k8s-007
|
|
1271
|
+
topic: kubernetes
|
|
1272
|
+
title: Use kubectl rollout to manage deployments
|
|
1273
|
+
body: >
|
|
1274
|
+
rollout commands let you check status, pause, resume, or undo deployments.
|
|
1275
|
+
Use undo to instantly roll back a bad release.
|
|
1276
|
+
example: |
|
|
1277
|
+
# Check rollout status
|
|
1278
|
+
kubectl rollout status deployment/my-app
|
|
1279
|
+
# Roll back to previous version
|
|
1280
|
+
kubectl rollout undo deployment/my-app
|
|
1281
|
+
# Roll back to specific revision
|
|
1282
|
+
kubectl rollout undo deployment/my-app --to-revision=3
|
|
1283
|
+
level: beginner
|
|
1284
|
+
source: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_rollout/
|
|
1285
|
+
|
|
1286
|
+
- id: k8s-008
|
|
1287
|
+
topic: kubernetes
|
|
1288
|
+
title: Use liveness and readiness probes
|
|
1289
|
+
body: >
|
|
1290
|
+
Liveness probes restart unhealthy containers. Readiness probes stop sending
|
|
1291
|
+
traffic to containers that aren't ready. Both are essential for reliability.
|
|
1292
|
+
example: |
|
|
1293
|
+
livenessProbe:
|
|
1294
|
+
httpGet:
|
|
1295
|
+
path: /healthz
|
|
1296
|
+
port: 8080
|
|
1297
|
+
initialDelaySeconds: 5
|
|
1298
|
+
periodSeconds: 10
|
|
1299
|
+
readinessProbe:
|
|
1300
|
+
httpGet:
|
|
1301
|
+
path: /ready
|
|
1302
|
+
port: 8080
|
|
1303
|
+
initialDelaySeconds: 3
|
|
1304
|
+
periodSeconds: 5
|
|
1305
|
+
level: intermediate
|
|
1306
|
+
source: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
|
|
1307
|
+
|
|
1308
|
+
- id: k8s-009
|
|
1309
|
+
topic: kubernetes
|
|
1310
|
+
title: Use kubectl debug for troubleshooting
|
|
1311
|
+
body: >
|
|
1312
|
+
kubectl debug creates ephemeral containers in a running pod or a copy of
|
|
1313
|
+
it. Debug without modifying the original pod spec.
|
|
1314
|
+
example: |
|
|
1315
|
+
# Attach a debug container to a running pod
|
|
1316
|
+
kubectl debug -it my-pod --image=busybox --target=my-container
|
|
1317
|
+
# Create a debug copy of a pod
|
|
1318
|
+
kubectl debug my-pod -it --copy-to=my-debug --container=debug
|
|
1319
|
+
level: advanced
|
|
1320
|
+
source: https://kubernetes.io/docs/tasks/debug/debug-application/debug-running-pod/
|
|
1321
|
+
|
|
1322
|
+
- id: k8s-010
|
|
1323
|
+
topic: kubernetes
|
|
1324
|
+
title: Use kubectl dry-run for safe testing
|
|
1325
|
+
body: >
|
|
1326
|
+
--dry-run=client validates your YAML without applying it. Combine with
|
|
1327
|
+
-o yaml to generate resource manifests from imperative commands.
|
|
1328
|
+
example: |
|
|
1329
|
+
# Validate without applying
|
|
1330
|
+
kubectl apply -f deployment.yaml --dry-run=client
|
|
1331
|
+
# Generate YAML from imperative command
|
|
1332
|
+
kubectl create deployment nginx --image=nginx \
|
|
1333
|
+
--dry-run=client -o yaml > deployment.yaml
|
|
1334
|
+
level: beginner
|
|
1335
|
+
source: https://kubernetes.io/docs/reference/kubectl/
|
|
1336
|
+
|
|
1337
|
+
# =============================================================================
|
|
1338
|
+
# Vim tips
|
|
1339
|
+
# =============================================================================
|
|
1340
|
+
|
|
1341
|
+
- id: vim-001
|
|
1342
|
+
topic: vim
|
|
1343
|
+
title: Use ciw to change a word quickly
|
|
1344
|
+
body: >
|
|
1345
|
+
`ciw` (change inner word) deletes the word under the cursor and enters
|
|
1346
|
+
insert mode. Works anywhere in the word, not just the start.
|
|
1347
|
+
example: |
|
|
1348
|
+
# Cursor on "hello" in: print("hello")
|
|
1349
|
+
ciw → deletes "hello", enter insert mode
|
|
1350
|
+
# Type new word: print("world")
|
|
1351
|
+
# Also: diw (delete), yiw (yank/copy)
|
|
1352
|
+
level: beginner
|
|
1353
|
+
source: https://vimhelp.org/motion.txt.html#text-objects
|
|
1354
|
+
|
|
1355
|
+
- id: vim-002
|
|
1356
|
+
topic: vim
|
|
1357
|
+
title: Use . to repeat the last change
|
|
1358
|
+
body: >
|
|
1359
|
+
The dot command repeats your last edit. Plan your edits so they're
|
|
1360
|
+
repeatable, then use . to apply them everywhere.
|
|
1361
|
+
example: |
|
|
1362
|
+
# Change "foo" to "bar" on current line
|
|
1363
|
+
/foo<Enter> → find "foo"
|
|
1364
|
+
cw bar<Esc> → change word to "bar"
|
|
1365
|
+
n → jump to next "foo"
|
|
1366
|
+
. → repeat the change
|
|
1367
|
+
level: beginner
|
|
1368
|
+
source: https://vimhelp.org/repeat.txt.html#single-repeat
|
|
1369
|
+
|
|
1370
|
+
- id: vim-003
|
|
1371
|
+
topic: vim
|
|
1372
|
+
title: Use visual block mode for column editing
|
|
1373
|
+
body: >
|
|
1374
|
+
Ctrl-v enters visual block mode for rectangular selections.
|
|
1375
|
+
Select a column, then insert, delete, or replace text across all lines.
|
|
1376
|
+
example: |
|
|
1377
|
+
# Add "//" comment prefix to lines 5-15:
|
|
1378
|
+
5G → go to line 5
|
|
1379
|
+
Ctrl-v → enter block mode
|
|
1380
|
+
10j → select down 10 lines
|
|
1381
|
+
I// <Esc> → insert "// " at the start of each line
|
|
1382
|
+
level: intermediate
|
|
1383
|
+
source: https://vimhelp.org/visual.txt.html#blockwise-visual
|
|
1384
|
+
|
|
1385
|
+
- id: vim-004
|
|
1386
|
+
topic: vim
|
|
1387
|
+
title: Use marks to jump between locations
|
|
1388
|
+
body: >
|
|
1389
|
+
Marks save cursor positions you can jump back to. Lowercase marks are
|
|
1390
|
+
per-file, uppercase marks work across files.
|
|
1391
|
+
example: |
|
|
1392
|
+
ma → set mark 'a' at current position
|
|
1393
|
+
'a → jump back to mark 'a' (line)
|
|
1394
|
+
`a → jump back to mark 'a' (exact position)
|
|
1395
|
+
'' → jump to position before last jump
|
|
1396
|
+
'. → jump to last edited line
|
|
1397
|
+
level: intermediate
|
|
1398
|
+
source: https://vimhelp.org/motion.txt.html#mark-motions
|
|
1399
|
+
|
|
1400
|
+
- id: vim-005
|
|
1401
|
+
topic: vim
|
|
1402
|
+
title: Use text objects for precise editing
|
|
1403
|
+
body: >
|
|
1404
|
+
Text objects select structured chunks of text. "i" means inner (without
|
|
1405
|
+
delimiters), "a" means around (with delimiters).
|
|
1406
|
+
example: |
|
|
1407
|
+
ci" → change inside quotes
|
|
1408
|
+
da( → delete around parentheses (including them)
|
|
1409
|
+
vi{ → visually select inside curly braces
|
|
1410
|
+
yat → yank (copy) around HTML tag
|
|
1411
|
+
cit → change inside HTML tag
|
|
1412
|
+
level: intermediate
|
|
1413
|
+
source: https://vimhelp.org/motion.txt.html#object-select
|
|
1414
|
+
|
|
1415
|
+
- id: vim-006
|
|
1416
|
+
topic: vim
|
|
1417
|
+
title: Use macros to automate repetitive edits
|
|
1418
|
+
body: >
|
|
1419
|
+
Record a sequence of commands as a macro, then replay it any number
|
|
1420
|
+
of times. Perfect for repetitive formatting or refactoring.
|
|
1421
|
+
example: |
|
|
1422
|
+
qa → start recording macro 'a'
|
|
1423
|
+
0 → go to start of line
|
|
1424
|
+
i"<Esc> → insert quote
|
|
1425
|
+
A",<Esc> → append quote and comma
|
|
1426
|
+
j → move to next line
|
|
1427
|
+
q → stop recording
|
|
1428
|
+
10@a → replay macro 10 times
|
|
1429
|
+
level: intermediate
|
|
1430
|
+
source: https://vimhelp.org/repeat.txt.html#complex-repeat
|
|
1431
|
+
|
|
1432
|
+
- id: vim-007
|
|
1433
|
+
topic: vim
|
|
1434
|
+
title: Use buffers instead of tabs
|
|
1435
|
+
body: >
|
|
1436
|
+
Buffers are Vim's way of holding open files. They're more efficient than
|
|
1437
|
+
tabs. Use :ls to list them and :b to switch.
|
|
1438
|
+
example: |
|
|
1439
|
+
:e file.py → open file in new buffer
|
|
1440
|
+
:ls → list all open buffers
|
|
1441
|
+
:b 3 → switch to buffer 3
|
|
1442
|
+
:b main → switch to buffer matching "main"
|
|
1443
|
+
:bd → close current buffer
|
|
1444
|
+
level: beginner
|
|
1445
|
+
source: https://vimhelp.org/windows.txt.html#buffers
|
|
1446
|
+
|
|
1447
|
+
- id: vim-008
|
|
1448
|
+
topic: vim
|
|
1449
|
+
title: Use :g for global commands on matching lines
|
|
1450
|
+
body: >
|
|
1451
|
+
The :g command executes an action on every line matching a pattern.
|
|
1452
|
+
It's like grep + sed combined inside Vim.
|
|
1453
|
+
example: |
|
|
1454
|
+
:g/TODO/d → delete all lines containing "TODO"
|
|
1455
|
+
:g/^$/d → delete all blank lines
|
|
1456
|
+
:g/debug/normal A // FIXME
|
|
1457
|
+
:g!/pattern/d → delete lines NOT matching pattern
|
|
1458
|
+
level: advanced
|
|
1459
|
+
source: https://vimhelp.org/repeat.txt.html#:global
|
|
1460
|
+
|
|
1461
|
+
- id: vim-009
|
|
1462
|
+
topic: vim
|
|
1463
|
+
title: Use Ctrl-o and Ctrl-i to navigate jumplist
|
|
1464
|
+
body: >
|
|
1465
|
+
Vim remembers every position you jump from. Ctrl-o goes back through
|
|
1466
|
+
your jump history, Ctrl-i goes forward.
|
|
1467
|
+
example: |
|
|
1468
|
+
# You're in file_a.py, jump to file_b.py
|
|
1469
|
+
Ctrl-o → jump back to file_a.py
|
|
1470
|
+
Ctrl-o → jump back further
|
|
1471
|
+
Ctrl-i → jump forward again
|
|
1472
|
+
:jumps → show full jump history
|
|
1473
|
+
level: beginner
|
|
1474
|
+
source: https://vimhelp.org/motion.txt.html#jumplist
|
|
1475
|
+
|
|
1476
|
+
- id: vim-010
|
|
1477
|
+
topic: vim
|
|
1478
|
+
title: Use :s for search and replace
|
|
1479
|
+
body: >
|
|
1480
|
+
Vim's substitute command is powerful and supports regex. Use flags to
|
|
1481
|
+
control scope and confirmation.
|
|
1482
|
+
example: |
|
|
1483
|
+
:s/old/new/ → replace first on current line
|
|
1484
|
+
:s/old/new/g → replace all on current line
|
|
1485
|
+
:%s/old/new/g → replace all in file
|
|
1486
|
+
:%s/old/new/gc → replace all with confirmation
|
|
1487
|
+
:5,20s/old/new/g → replace in lines 5-20
|
|
1488
|
+
level: beginner
|
|
1489
|
+
source: https://vimhelp.org/change.txt.html#:substitute
|
|
1490
|
+
|
|
1491
|
+
# =============================================================================
|
|
1492
|
+
# JavaScript tips
|
|
1493
|
+
# =============================================================================
|
|
1494
|
+
|
|
1495
|
+
- id: js-001
|
|
1496
|
+
topic: javascript
|
|
1497
|
+
title: Use optional chaining to access nested properties
|
|
1498
|
+
body: >
|
|
1499
|
+
The ?. operator short-circuits to undefined if any part of the chain
|
|
1500
|
+
is null or undefined. No more "Cannot read property of undefined" errors.
|
|
1501
|
+
example: |
|
|
1502
|
+
// Instead of: user && user.address && user.address.city
|
|
1503
|
+
const city = user?.address?.city;
|
|
1504
|
+
const first = arr?.[0];
|
|
1505
|
+
const result = obj?.method?.();
|
|
1506
|
+
level: beginner
|
|
1507
|
+
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
|
|
1508
|
+
|
|
1509
|
+
- id: js-002
|
|
1510
|
+
topic: javascript
|
|
1511
|
+
title: Use destructuring for cleaner function parameters
|
|
1512
|
+
body: >
|
|
1513
|
+
Destructuring in function parameters makes it clear what a function
|
|
1514
|
+
expects and provides easy defaults.
|
|
1515
|
+
example: |
|
|
1516
|
+
function createUser({ name, role = "viewer", active = true }) {
|
|
1517
|
+
return { name, role, active };
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
createUser({ name: "Alice" });
|
|
1521
|
+
// { name: "Alice", role: "viewer", active: true }
|
|
1522
|
+
level: beginner
|
|
1523
|
+
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
|
|
1524
|
+
|
|
1525
|
+
- id: js-003
|
|
1526
|
+
topic: javascript
|
|
1527
|
+
title: Use Promise.all for parallel async operations
|
|
1528
|
+
body: >
|
|
1529
|
+
Promise.all runs multiple promises concurrently and waits for all to
|
|
1530
|
+
complete. Much faster than awaiting them sequentially.
|
|
1531
|
+
example: |
|
|
1532
|
+
// Slow: sequential
|
|
1533
|
+
const users = await fetchUsers();
|
|
1534
|
+
const posts = await fetchPosts();
|
|
1535
|
+
|
|
1536
|
+
// Fast: parallel
|
|
1537
|
+
const [users, posts] = await Promise.all([
|
|
1538
|
+
fetchUsers(),
|
|
1539
|
+
fetchPosts(),
|
|
1540
|
+
]);
|
|
1541
|
+
level: intermediate
|
|
1542
|
+
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
|
|
1543
|
+
|
|
1544
|
+
- id: js-004
|
|
1545
|
+
topic: javascript
|
|
1546
|
+
title: Use Array.from for array-like conversions
|
|
1547
|
+
body: >
|
|
1548
|
+
Array.from converts iterables and array-like objects to real arrays.
|
|
1549
|
+
Its second argument is a map function.
|
|
1550
|
+
example: |
|
|
1551
|
+
// NodeList to Array
|
|
1552
|
+
const divs = Array.from(document.querySelectorAll("div"));
|
|
1553
|
+
|
|
1554
|
+
// Generate a sequence
|
|
1555
|
+
const nums = Array.from({ length: 5 }, (_, i) => i + 1);
|
|
1556
|
+
// [1, 2, 3, 4, 5]
|
|
1557
|
+
level: beginner
|
|
1558
|
+
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
|
|
1559
|
+
|
|
1560
|
+
- id: js-005
|
|
1561
|
+
topic: javascript
|
|
1562
|
+
title: Use structuredClone for deep copying
|
|
1563
|
+
body: >
|
|
1564
|
+
structuredClone() creates a true deep copy of objects, handling nested
|
|
1565
|
+
objects, arrays, Maps, Sets, and Dates correctly.
|
|
1566
|
+
example: |
|
|
1567
|
+
const original = { user: { name: "Alice" }, tags: [1, 2] };
|
|
1568
|
+
|
|
1569
|
+
// Shallow copy (nested objects still shared)
|
|
1570
|
+
const shallow = { ...original };
|
|
1571
|
+
|
|
1572
|
+
// Deep copy (fully independent)
|
|
1573
|
+
const deep = structuredClone(original);
|
|
1574
|
+
deep.user.name = "Bob"; // original unchanged
|
|
1575
|
+
level: intermediate
|
|
1576
|
+
source: https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone
|
|
1577
|
+
|
|
1578
|
+
- id: js-006
|
|
1579
|
+
topic: javascript
|
|
1580
|
+
title: Use AbortController to cancel fetch requests
|
|
1581
|
+
body: >
|
|
1582
|
+
AbortController lets you cancel in-flight fetch requests. Essential for
|
|
1583
|
+
search-as-you-type and component unmounting.
|
|
1584
|
+
example: |
|
|
1585
|
+
const controller = new AbortController();
|
|
1586
|
+
|
|
1587
|
+
fetch("/api/data", { signal: controller.signal })
|
|
1588
|
+
.then(res => res.json())
|
|
1589
|
+
.catch(err => {
|
|
1590
|
+
if (err.name === "AbortError") return; // expected
|
|
1591
|
+
throw err;
|
|
1592
|
+
});
|
|
1593
|
+
|
|
1594
|
+
// Cancel the request
|
|
1595
|
+
controller.abort();
|
|
1596
|
+
level: intermediate
|
|
1597
|
+
source: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
|
|
1598
|
+
|
|
1599
|
+
- id: js-007
|
|
1600
|
+
topic: javascript
|
|
1601
|
+
title: Use Map instead of objects for dynamic keys
|
|
1602
|
+
body: >
|
|
1603
|
+
Map preserves insertion order, supports any key type (not just strings),
|
|
1604
|
+
and has better performance for frequent additions/deletions.
|
|
1605
|
+
example: |
|
|
1606
|
+
const cache = new Map();
|
|
1607
|
+
cache.set(userObj, "data"); // objects as keys!
|
|
1608
|
+
cache.set(42, "answer"); // numbers as keys
|
|
1609
|
+
|
|
1610
|
+
cache.has(userObj); // true
|
|
1611
|
+
cache.size; // 2
|
|
1612
|
+
cache.delete(42);
|
|
1613
|
+
|
|
1614
|
+
for (const [key, value] of cache) { ... }
|
|
1615
|
+
level: intermediate
|
|
1616
|
+
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
|
1617
|
+
|
|
1618
|
+
- id: js-008
|
|
1619
|
+
topic: javascript
|
|
1620
|
+
title: Use at() for negative array indexing
|
|
1621
|
+
body: >
|
|
1622
|
+
The at() method supports negative indices to count from the end.
|
|
1623
|
+
Cleaner than arr[arr.length - 1] for getting the last element.
|
|
1624
|
+
example: |
|
|
1625
|
+
const arr = [1, 2, 3, 4, 5];
|
|
1626
|
+
arr.at(0); // 1
|
|
1627
|
+
arr.at(-1); // 5 (last)
|
|
1628
|
+
arr.at(-2); // 4 (second to last)
|
|
1629
|
+
|
|
1630
|
+
// Works on strings too
|
|
1631
|
+
"hello".at(-1); // "o"
|
|
1632
|
+
level: beginner
|
|
1633
|
+
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at
|
|
1634
|
+
|
|
1635
|
+
- id: js-009
|
|
1636
|
+
topic: javascript
|
|
1637
|
+
title: Use Object.groupBy to group array items
|
|
1638
|
+
body: >
|
|
1639
|
+
Object.groupBy groups array elements by a key function. Replaces
|
|
1640
|
+
manual reduce-based grouping patterns.
|
|
1641
|
+
example: |
|
|
1642
|
+
const people = [
|
|
1643
|
+
{ name: "Alice", dept: "eng" },
|
|
1644
|
+
{ name: "Bob", dept: "sales" },
|
|
1645
|
+
{ name: "Charlie", dept: "eng" },
|
|
1646
|
+
];
|
|
1647
|
+
|
|
1648
|
+
const byDept = Object.groupBy(people, p => p.dept);
|
|
1649
|
+
// { eng: [{Alice}, {Charlie}], sales: [{Bob}] }
|
|
1650
|
+
level: intermediate
|
|
1651
|
+
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy
|
|
1652
|
+
|
|
1653
|
+
- id: js-010
|
|
1654
|
+
topic: javascript
|
|
1655
|
+
title: Use WeakRef for memory-safe caching
|
|
1656
|
+
body: >
|
|
1657
|
+
WeakRef holds a reference that doesn't prevent garbage collection.
|
|
1658
|
+
Combine with FinalizationRegistry for automatic cache cleanup.
|
|
1659
|
+
example: |
|
|
1660
|
+
const cache = new Map();
|
|
1661
|
+
|
|
1662
|
+
function getCached(key, compute) {
|
|
1663
|
+
const ref = cache.get(key);
|
|
1664
|
+
const value = ref?.deref();
|
|
1665
|
+
if (value !== undefined) return value;
|
|
1666
|
+
|
|
1667
|
+
const result = compute();
|
|
1668
|
+
cache.set(key, new WeakRef(result));
|
|
1669
|
+
return result;
|
|
1670
|
+
}
|
|
1671
|
+
level: advanced
|
|
1672
|
+
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef
|
|
1673
|
+
|
|
1674
|
+
# =============================================================================
|
|
1675
|
+
# Terraform tips
|
|
1676
|
+
# =============================================================================
|
|
1677
|
+
|
|
1678
|
+
- id: tf-001
|
|
1679
|
+
topic: terraform
|
|
1680
|
+
title: Use variables with validation rules
|
|
1681
|
+
body: >
|
|
1682
|
+
Variable blocks support validation conditions that catch bad input
|
|
1683
|
+
at plan time, before any resources are created.
|
|
1684
|
+
example: |
|
|
1685
|
+
variable "environment" {
|
|
1686
|
+
type = string
|
|
1687
|
+
validation {
|
|
1688
|
+
condition = contains(["dev", "staging", "prod"], var.environment)
|
|
1689
|
+
error_message = "Must be dev, staging, or prod."
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
level: beginner
|
|
1693
|
+
source: https://developer.hashicorp.com/terraform/language/values/variables
|
|
1694
|
+
|
|
1695
|
+
- id: tf-002
|
|
1696
|
+
topic: terraform
|
|
1697
|
+
title: Use terraform fmt to format code
|
|
1698
|
+
body: >
|
|
1699
|
+
`terraform fmt` auto-formats your .tf files to a consistent style.
|
|
1700
|
+
Run it before every commit. Use -check in CI to enforce it.
|
|
1701
|
+
example: |
|
|
1702
|
+
# Format current directory
|
|
1703
|
+
terraform fmt
|
|
1704
|
+
# Format recursively
|
|
1705
|
+
terraform fmt -recursive
|
|
1706
|
+
# Check only (for CI) - exit 1 if changes needed
|
|
1707
|
+
terraform fmt -check -recursive
|
|
1708
|
+
level: beginner
|
|
1709
|
+
source: https://developer.hashicorp.com/terraform/cli/commands/fmt
|
|
1710
|
+
|
|
1711
|
+
- id: tf-003
|
|
1712
|
+
topic: terraform
|
|
1713
|
+
title: Use locals to reduce repetition
|
|
1714
|
+
body: >
|
|
1715
|
+
Locals let you define computed values used in multiple places.
|
|
1716
|
+
They simplify complex expressions and reduce copy-paste errors.
|
|
1717
|
+
example: |
|
|
1718
|
+
locals {
|
|
1719
|
+
common_tags = {
|
|
1720
|
+
Environment = var.environment
|
|
1721
|
+
Project = var.project_name
|
|
1722
|
+
ManagedBy = "terraform"
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
resource "aws_instance" "web" {
|
|
1727
|
+
tags = merge(local.common_tags, { Name = "web-server" })
|
|
1728
|
+
}
|
|
1729
|
+
level: beginner
|
|
1730
|
+
source: https://developer.hashicorp.com/terraform/language/values/locals
|
|
1731
|
+
|
|
1732
|
+
- id: tf-004
|
|
1733
|
+
topic: terraform
|
|
1734
|
+
title: Use terraform plan -out to save plans
|
|
1735
|
+
body: >
|
|
1736
|
+
Save the plan to a file, then apply exactly that plan. This prevents
|
|
1737
|
+
drift between planning and applying in CI/CD pipelines.
|
|
1738
|
+
example: |
|
|
1739
|
+
# Save the plan
|
|
1740
|
+
terraform plan -out=tfplan
|
|
1741
|
+
# Apply exactly what was planned
|
|
1742
|
+
terraform apply tfplan
|
|
1743
|
+
# In CI: plan in one step, review, then apply
|
|
1744
|
+
level: intermediate
|
|
1745
|
+
source: https://developer.hashicorp.com/terraform/cli/commands/plan
|
|
1746
|
+
|
|
1747
|
+
- id: tf-005
|
|
1748
|
+
topic: terraform
|
|
1749
|
+
title: Use data sources to reference existing resources
|
|
1750
|
+
body: >
|
|
1751
|
+
Data sources let you read information about infrastructure that already
|
|
1752
|
+
exists, without managing it. Reference it like any other resource.
|
|
1753
|
+
example: |
|
|
1754
|
+
data "aws_vpc" "main" {
|
|
1755
|
+
filter {
|
|
1756
|
+
name = "tag:Name"
|
|
1757
|
+
values = ["main-vpc"]
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
resource "aws_subnet" "app" {
|
|
1762
|
+
vpc_id = data.aws_vpc.main.id
|
|
1763
|
+
# ...
|
|
1764
|
+
}
|
|
1765
|
+
level: intermediate
|
|
1766
|
+
source: https://developer.hashicorp.com/terraform/language/data-sources
|
|
1767
|
+
|
|
1768
|
+
- id: tf-006
|
|
1769
|
+
topic: terraform
|
|
1770
|
+
title: Use for_each instead of count for resources
|
|
1771
|
+
body: >
|
|
1772
|
+
for_each creates resources keyed by map or set, so removing an item
|
|
1773
|
+
only destroys that resource. count-based resources shift indexes.
|
|
1774
|
+
example: |
|
|
1775
|
+
variable "buckets" {
|
|
1776
|
+
default = {
|
|
1777
|
+
logs = "my-logs-bucket"
|
|
1778
|
+
assets = "my-assets-bucket"
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
resource "aws_s3_bucket" "this" {
|
|
1783
|
+
for_each = var.buckets
|
|
1784
|
+
bucket = each.value
|
|
1785
|
+
tags = { Name = each.key }
|
|
1786
|
+
}
|
|
1787
|
+
level: intermediate
|
|
1788
|
+
source: https://developer.hashicorp.com/terraform/language/meta-arguments/for_each
|
|
1789
|
+
|
|
1790
|
+
- id: tf-007
|
|
1791
|
+
topic: terraform
|
|
1792
|
+
title: Use lifecycle to prevent accidental destruction
|
|
1793
|
+
body: >
|
|
1794
|
+
The prevent_destroy lifecycle rule stops Terraform from destroying
|
|
1795
|
+
critical resources like databases or storage buckets.
|
|
1796
|
+
example: |
|
|
1797
|
+
resource "aws_db_instance" "main" {
|
|
1798
|
+
# ...
|
|
1799
|
+
lifecycle {
|
|
1800
|
+
prevent_destroy = true
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
# Also useful: create_before_destroy for zero-downtime
|
|
1805
|
+
lifecycle {
|
|
1806
|
+
create_before_destroy = true
|
|
1807
|
+
}
|
|
1808
|
+
level: intermediate
|
|
1809
|
+
source: https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle
|
|
1810
|
+
|
|
1811
|
+
- id: tf-008
|
|
1812
|
+
topic: terraform
|
|
1813
|
+
title: Use moved blocks for safe refactoring
|
|
1814
|
+
body: >
|
|
1815
|
+
The moved block tells Terraform that a resource was renamed, not deleted
|
|
1816
|
+
and recreated. Prevents accidental destruction during refactors.
|
|
1817
|
+
example: |
|
|
1818
|
+
# Renamed aws_instance.web to aws_instance.app
|
|
1819
|
+
moved {
|
|
1820
|
+
from = aws_instance.web
|
|
1821
|
+
to = aws_instance.app
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
resource "aws_instance" "app" {
|
|
1825
|
+
# ...
|
|
1826
|
+
}
|
|
1827
|
+
level: advanced
|
|
1828
|
+
source: https://developer.hashicorp.com/terraform/language/modules/develop/refactoring
|
|
1829
|
+
|
|
1830
|
+
- id: tf-009
|
|
1831
|
+
topic: terraform
|
|
1832
|
+
title: Use terraform import to adopt existing resources
|
|
1833
|
+
body: >
|
|
1834
|
+
`terraform import` brings an existing resource under Terraform management.
|
|
1835
|
+
In Terraform 1.5+, you can use import blocks declaratively.
|
|
1836
|
+
example: |
|
|
1837
|
+
# Declarative import (1.5+)
|
|
1838
|
+
import {
|
|
1839
|
+
to = aws_instance.web
|
|
1840
|
+
id = "i-abc123"
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
# Then run: terraform plan
|
|
1844
|
+
# Terraform generates the config diff
|
|
1845
|
+
level: advanced
|
|
1846
|
+
source: https://developer.hashicorp.com/terraform/language/import
|
|
1847
|
+
|
|
1848
|
+
- id: tf-010
|
|
1849
|
+
topic: terraform
|
|
1850
|
+
title: Use terraform state list to audit resources
|
|
1851
|
+
body: >
|
|
1852
|
+
`terraform state list` shows all resources Terraform manages.
|
|
1853
|
+
Use it to audit what's tracked and find resources to remove or move.
|
|
1854
|
+
example: |
|
|
1855
|
+
# List all managed resources
|
|
1856
|
+
terraform state list
|
|
1857
|
+
# Filter by type
|
|
1858
|
+
terraform state list | grep aws_instance
|
|
1859
|
+
# Show details of one resource
|
|
1860
|
+
terraform state show aws_instance.web
|
|
1861
|
+
# Remove from state (without destroying)
|
|
1862
|
+
terraform state rm aws_instance.legacy
|
|
1863
|
+
level: beginner
|
|
1864
|
+
source: https://developer.hashicorp.com/terraform/cli/commands/state/list
|
|
1865
|
+
|
|
1866
|
+
# =============================================================================
|
|
1867
|
+
# Rust tips
|
|
1868
|
+
# =============================================================================
|
|
1869
|
+
|
|
1870
|
+
- id: rust-001
|
|
1871
|
+
topic: rust
|
|
1872
|
+
title: Use if let for single-pattern matching
|
|
1873
|
+
body: >
|
|
1874
|
+
`if let` is cleaner than match when you only care about one variant.
|
|
1875
|
+
Great for Option and Result handling.
|
|
1876
|
+
example: |
|
|
1877
|
+
// Instead of:
|
|
1878
|
+
match config.get("port") {
|
|
1879
|
+
Some(port) => println!("Port: {port}"),
|
|
1880
|
+
None => {}
|
|
1881
|
+
}
|
|
1882
|
+
// Use:
|
|
1883
|
+
if let Some(port) = config.get("port") {
|
|
1884
|
+
println!("Port: {port}");
|
|
1885
|
+
}
|
|
1886
|
+
level: beginner
|
|
1887
|
+
source: https://doc.rust-lang.org/book/ch06-03-if-let.html
|
|
1888
|
+
|
|
1889
|
+
- id: rust-002
|
|
1890
|
+
topic: rust
|
|
1891
|
+
title: Use the ? operator for error propagation
|
|
1892
|
+
body: >
|
|
1893
|
+
The ? operator returns early with the error if a Result is Err, or
|
|
1894
|
+
unwraps the Ok value. Replaces verbose match/unwrap chains.
|
|
1895
|
+
example: |
|
|
1896
|
+
fn read_config(path: &str) -> Result<Config, Box<dyn Error>> {
|
|
1897
|
+
let contents = fs::read_to_string(path)?;
|
|
1898
|
+
let config: Config = toml::from_str(&contents)?;
|
|
1899
|
+
Ok(config)
|
|
1900
|
+
}
|
|
1901
|
+
level: beginner
|
|
1902
|
+
source: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html
|
|
1903
|
+
|
|
1904
|
+
- id: rust-003
|
|
1905
|
+
topic: rust
|
|
1906
|
+
title: Use iterators instead of manual loops
|
|
1907
|
+
body: >
|
|
1908
|
+
Rust iterators are zero-cost abstractions. Chaining map, filter, and
|
|
1909
|
+
collect is both idiomatic and as fast as hand-written loops.
|
|
1910
|
+
example: |
|
|
1911
|
+
let names: Vec<String> = users
|
|
1912
|
+
.iter()
|
|
1913
|
+
.filter(|u| u.active)
|
|
1914
|
+
.map(|u| u.name.clone())
|
|
1915
|
+
.collect();
|
|
1916
|
+
|
|
1917
|
+
let total: f64 = prices.iter().sum();
|
|
1918
|
+
level: intermediate
|
|
1919
|
+
source: https://doc.rust-lang.org/book/ch13-02-iterators.html
|
|
1920
|
+
|
|
1921
|
+
- id: rust-004
|
|
1922
|
+
topic: rust
|
|
1923
|
+
title: Use derive macros for common traits
|
|
1924
|
+
body: >
|
|
1925
|
+
#[derive] auto-implements traits like Debug, Clone, PartialEq, and
|
|
1926
|
+
Serialize. Saves pages of boilerplate.
|
|
1927
|
+
example: |
|
|
1928
|
+
#[derive(Debug, Clone, PartialEq, serde::Serialize)]
|
|
1929
|
+
struct User {
|
|
1930
|
+
name: String,
|
|
1931
|
+
age: u32,
|
|
1932
|
+
active: bool,
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
// Now you can: println!("{:?}", user);
|
|
1936
|
+
// And: user1 == user2
|
|
1937
|
+
level: beginner
|
|
1938
|
+
source: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html
|
|
1939
|
+
|
|
1940
|
+
- id: rust-005
|
|
1941
|
+
topic: rust
|
|
1942
|
+
title: Use pattern matching with enums
|
|
1943
|
+
body: >
|
|
1944
|
+
Rust enums can hold data in each variant. Combined with match, they
|
|
1945
|
+
replace class hierarchies and visitor patterns.
|
|
1946
|
+
example: |
|
|
1947
|
+
enum Shape {
|
|
1948
|
+
Circle(f64),
|
|
1949
|
+
Rect(f64, f64),
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
fn area(shape: &Shape) -> f64 {
|
|
1953
|
+
match shape {
|
|
1954
|
+
Shape::Circle(r) => std::f64::consts::PI * r * r,
|
|
1955
|
+
Shape::Rect(w, h) => w * h,
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
level: intermediate
|
|
1959
|
+
source: https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html
|
|
1960
|
+
|
|
1961
|
+
- id: rust-006
|
|
1962
|
+
topic: rust
|
|
1963
|
+
title: Use clippy for idiomatic Rust
|
|
1964
|
+
body: >
|
|
1965
|
+
Clippy catches common mistakes and suggests more idiomatic code.
|
|
1966
|
+
Run it regularly — it's like having a Rust expert review your code.
|
|
1967
|
+
example: |
|
|
1968
|
+
# Run clippy
|
|
1969
|
+
cargo clippy
|
|
1970
|
+
# Treat all warnings as errors (for CI)
|
|
1971
|
+
cargo clippy -- -D warnings
|
|
1972
|
+
# Fix automatically where possible
|
|
1973
|
+
cargo clippy --fix
|
|
1974
|
+
level: beginner
|
|
1975
|
+
source: https://doc.rust-lang.org/clippy/
|
|
1976
|
+
|
|
1977
|
+
- id: rust-007
|
|
1978
|
+
topic: rust
|
|
1979
|
+
title: Use impl blocks with associated functions
|
|
1980
|
+
body: >
|
|
1981
|
+
Associated functions (no &self) are Rust's constructors. By convention,
|
|
1982
|
+
new() creates an instance, but you can name them anything.
|
|
1983
|
+
example: |
|
|
1984
|
+
struct Config {
|
|
1985
|
+
port: u16,
|
|
1986
|
+
debug: bool,
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
impl Config {
|
|
1990
|
+
fn new(port: u16) -> Self {
|
|
1991
|
+
Self { port, debug: false }
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
fn with_debug(mut self) -> Self {
|
|
1995
|
+
self.debug = true;
|
|
1996
|
+
self
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
let cfg = Config::new(8080).with_debug();
|
|
2001
|
+
level: intermediate
|
|
2002
|
+
source: https://doc.rust-lang.org/book/ch05-03-method-syntax.html
|
|
2003
|
+
|
|
2004
|
+
- id: rust-008
|
|
2005
|
+
topic: rust
|
|
2006
|
+
title: Use Option combinators instead of match
|
|
2007
|
+
body: >
|
|
2008
|
+
Option has methods like map, and_then, unwrap_or, and filter that chain
|
|
2009
|
+
cleanly. Prefer these over verbose match expressions.
|
|
2010
|
+
example: |
|
|
2011
|
+
// Instead of matching:
|
|
2012
|
+
let name = user
|
|
2013
|
+
.get("name")
|
|
2014
|
+
.map(|n| n.to_uppercase())
|
|
2015
|
+
.unwrap_or_else(|| "ANONYMOUS".to_string());
|
|
2016
|
+
|
|
2017
|
+
// Chain with and_then for nested Options
|
|
2018
|
+
let city = user.address.as_ref().and_then(|a| a.city.as_ref());
|
|
2019
|
+
level: intermediate
|
|
2020
|
+
source: https://doc.rust-lang.org/std/option/enum.Option.html
|
|
2021
|
+
|
|
2022
|
+
- id: rust-009
|
|
2023
|
+
topic: rust
|
|
2024
|
+
title: Use cargo test with module tests
|
|
2025
|
+
body: >
|
|
2026
|
+
Rust tests live right next to the code in a #[cfg(test)] module.
|
|
2027
|
+
Private functions are testable without any special setup.
|
|
2028
|
+
example: |
|
|
2029
|
+
fn add(a: i32, b: i32) -> i32 { a + b }
|
|
2030
|
+
|
|
2031
|
+
#[cfg(test)]
|
|
2032
|
+
mod tests {
|
|
2033
|
+
use super::*;
|
|
2034
|
+
|
|
2035
|
+
#[test]
|
|
2036
|
+
fn test_add() {
|
|
2037
|
+
assert_eq!(add(2, 3), 5);
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
#[test]
|
|
2041
|
+
#[should_panic]
|
|
2042
|
+
fn test_overflow() {
|
|
2043
|
+
add(i32::MAX, 1);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
level: beginner
|
|
2047
|
+
source: https://doc.rust-lang.org/book/ch11-01-writing-tests.html
|
|
2048
|
+
|
|
2049
|
+
- id: rust-010
|
|
2050
|
+
topic: rust
|
|
2051
|
+
title: Use traits for shared behavior
|
|
2052
|
+
body: >
|
|
2053
|
+
Traits define shared interfaces. Any type can implement a trait, enabling
|
|
2054
|
+
polymorphism without inheritance.
|
|
2055
|
+
example: |
|
|
2056
|
+
trait Summary {
|
|
2057
|
+
fn summarize(&self) -> String;
|
|
2058
|
+
// Default implementation
|
|
2059
|
+
fn preview(&self) -> String {
|
|
2060
|
+
format!("{}...", &self.summarize()[..50])
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
impl Summary for Article {
|
|
2065
|
+
fn summarize(&self) -> String {
|
|
2066
|
+
format!("{} by {}", self.title, self.author)
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
level: intermediate
|
|
2070
|
+
source: https://doc.rust-lang.org/book/ch10-02-traits.html
|