This is an article from a series of blog postings. Please do read my "Using Org Mode Features" (UOMF) series page for explanations on articles of this series.
In my "How to choose a tool" article, I describe the general approach how I do think that you should make tool choices.
Well, it's not just tool choices - the process can be generalized to any choice making process, if you think about it.
In order to support this process for a few steps, I generated an Org-mode yankpad template which minimizes the manual effort to create the boilerplate.
How the template works:
- Instantiate the template using yankpad
- Tab stops during expansion (yasnippet
$1–$4,$0): a.$1— date prefix for#+NAMEIDs (auto-filled with today's date, mirrors to all references) b.$2— project title in the heading c.$3,$4— first two solution names d.$0— final cursor lands in the first requirement cell - Heading levels adapt automatically via org-current-level
- Note the
$escaping: the\$1in the#+TBLFMline is literal Org column$1, not a yasnippet reference
Workflow after expansion:
- Fill in requirements (rows) in the first table — feature name, class (-2..2), weight (≥1)
- Add matching rows to the evaluation table, then
C-c C-con the#+TBLFMto pull feature names - Add solution columns with
M-S-rightas needed - Fill in coverage values (0/1/2)
C-c C-con the source block → breakdown + ranked results
Requirement Analysis: Example Vacation Project
This is how it looks, when applied. Notice that in that example, I omitted the optional weight feature in the "Feature" table to keep it simple. Furthermore, don't be offended by my example rating: it's just for demo purposes.
Requirements
Define features and classify them by importance.
Class (integer,
-2to2): how important is this feature?-2 must not have -1 don't want 0 neutral 1 nice to have 2 must have Weight (integer ≥ 1): optional amplifier within a class. Leave at
1if all requirements in a class are equally important.
| Feature | Class | Weight |
|---|---|---|
| Good price | 1 | 1 |
| Good weather | 2 | |
| Availability of ocean | 1 | |
| Cultural possbilities | 1 |
Solution Evaluation
Rate how strongly each solution exhibits each feature.
Coverage (integer,
0to2):0 not present 1 somewhat present 2 fully present To add solutions: insert columns with
M-S-<right>.Feature names auto-fill: press
C-c C-con theTBLFMline.Ensure this table has the same number of data rows as the requirements table.
| Feature | Vienna | Bahamas | Stockholm |
|---|---|---|---|
| Good price | 1 | 0 | 1 |
| Good weather | 1 | 2 | 0 |
| Availability of ocean | 0 | 2 | 1 |
| Cultural possbilities | 2 | 0 | 1 |
Results
Scoring (higher is better):
- Class ≥ 0:
class × weight × coverage - Class < 0:
|class| × weight × (1 − coverage)
Press C-c C-c on the source block to compute results.
2026-03-22-example-results
reqs = [r for r in reqs if r is not None]
ev = [r for r in ev if r is not None]
solutions = ev[0][1:]
req_rows = reqs[1:]
ev_rows = ev[1:]
h = "| Requirement | Cl | W |"
for s in solutions:
h += " {} cov | pts |".format(s)
lines = [h, "|-"]
totals = [0] * len(solutions)
for i, rr in enumerate(req_rows):
feat = rr[0]
cls = int(rr[1])
try:
w = int(rr[2])
except (ValueError, TypeError):
w = 1
row = "| {} | {} | {} |".format(feat, cls, w)
er = ev_rows[i]
for j in range(len(solutions)):
cov = int(er[1 + j])
if cls >= 0:
pts = cls * w * cov
else:
pts = abs(cls) * w * (1 - cov)
totals[j] += pts
row += " {} | {:+d} |".format(cov, pts)
lines.append(row)
lines.append("|-")
total_row = "| *Total* | | |"
for t in totals:
total_row += " | *{:+d}* |".format(t)
lines.append(total_row)
lines += ["", "| Rank | Solution | Score |", "|-"]
ranked = sorted(zip(solutions, totals), key=lambda x: -x[1])
for rank, (sol, sc) in enumerate(ranked, 1):
lines.append("| {} | {} | {:+d} |".format(rank, sol, sc))
return "\n".join(lines)
#+RESULTS: 2026-03-22-example-results
| Requirement | Cl | W | Vienna cov | pts | Bahamas cov | pts | Stockholm cov | pts |
|---|---|---|---|---|---|---|---|---|
| Good price | 1 | 1 | 1 | +1 | 0 | +0 | 1 | +1 |
| Good weather | 2 | 1 | 1 | +2 | 2 | +4 | 0 | +0 |
| Availability of ocean | 1 | 1 | 0 | +0 | 2 | +2 | 1 | +1 |
| Cultural possbilities | 1 | 1 | 2 | +2 | 0 | +0 | 1 | +1 |
| Total | +5 | +6 | +3 |
| Rank | Solution | Score |
|---|---|---|
| 1 | Bahamas | +6 |
| 2 | Vienna | +5 |
| 3 | Stockholm | +3 |
The Template
Here is the raw template. If you're new to yankpad, please do refer to the project's documentation how to use it. It's very similar to yasnippet but its definitions are managed within an Orgdown file.
`(progn (setq yas--ra-h1 (make-string (+ 1 (or (org-current-level) 0)) ?*) yas--ra-h2 (make-string (+ 2 (or (org-current-level) 0)) ?*)) "")`
`yas--ra-h1` Requirement Analysis: ${2:Project Title}
`yas--ra-h2` Requirements
Define features and classify them by importance.
- *Class* (integer, =-2= to =2=): how important is this feature?
| -2 | must not have |
| -1 | don't want |
| 0 | neutral |
| 1 | nice to have |
| 2 | must have |
- *Weight* (integer ≥ 1): optional amplifier within a class.
Leave at =1= if all requirements in a class are equally important.
#+NAME: ${1:`(format-time-string "%Y-%m-%d")`}-requirements
| Feature | Class | Weight |
|---------+-------+--------|
| $0 | | 1 |
`yas--ra-h2` Solution Evaluation
Rate how strongly each solution exhibits each feature.
- *Coverage* (integer, =0= to =2=):
| 0 | not present |
| 1 | somewhat present |
| 2 | fully present |
- To add solutions: insert columns with =M-S-<right>=.
- Feature names auto-fill: press =C-c C-c= on the =TBLFM= line.
- Ensure this table has the same number of data rows as the requirements table.
#+NAME: $1-evaluation
| Feature | ${3:Solution A} | ${4:Solution B} |
|---------+-----------------+-----------------+
| | | |
#+TBLFM: \$1=remote($1-requirements, @@#\$1)
`yas--ra-h2` Results
Scoring (higher is better):
- Class ≥ 0: =class × weight × coverage=
- Class < 0: =|class| × weight × (1 − coverage)=
Press =C-c C-c= on the source block to compute results.
#+NAME: $1-results
#+BEGIN_SRC python :var reqs=$1-requirements ev=$1-evaluation :colnames no :results value raw
reqs = [r for r in reqs if r is not None]
ev = [r for r in ev if r is not None]
solutions = ev[0][1:]
req_rows = reqs[1:]
ev_rows = ev[1:]
h = "| Requirement | Cl | W |"
for s in solutions:
h += " {} cov | pts |".format(s)
lines = [h, "|-"]
totals = [0] * len(solutions)
for i, rr in enumerate(req_rows):
feat = rr[0]
cls = int(rr[1])
try:
w = int(rr[2])
except (ValueError, TypeError):
w = 1
row = "| {} | {} | {} |".format(feat, cls, w)
er = ev_rows[i]
for j in range(len(solutions)):
cov = int(er[1 + j])
if cls >= 0:
pts = cls * w * cov
else:
pts = abs(cls) * w * (1 - cov)
totals[j] += pts
row += " {} | {:+d} |".format(cov, pts)
lines.append(row)
lines.append("|-")
total_row = "| *Total* | | |"
for t in totals:
total_row += " | *{:+d}* |".format(t)
lines.append(total_row)
lines += ["", "| Rank | Solution | Score |", "|-"]
ranked = sorted(zip(solutions, totals), key=lambda x: -x[1])
for rank, (sol, sc) in enumerate(ranked, 1):
lines.append("| {} | {} | {:+d} |".format(rank, sol, sc))
return "\n".join(lines)
#+END_SRC