babel · 15m

Executable documents with Babel

Babel does not turn Org into a proprietary notebook; it evaluates marked source blocks and writes results back according to declared rules.

Org Babel connects prose, code, data, and results in one file. Insert a source block with C-c C-, s and choose a language, or type the delimiters yourself:

#+begin_src python
print("Hello, Org")
#+end_src

Place point inside and press C-c C-c (org-babel-execute-src-block). Org asks for confirmation, then inserts results according to the block's header arguments.

Enable only the languages you use:

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (python . t)
   (shell . t)
   (sql . t)
   (js . t)))

Enabling a backend does not install its external runtime. ob-js, ob-shell, and a SQL client each still need their interpreter and connection settings present.

Header arguments

Header arguments govern evaluation. Common ones are :results value|output, :exports code|results|both|none, :session, :var, :dir, :file, :cache yes, and :tangle. :results output captures printed standard output; :results value serializes the language's returned value. Confusing the two produces blank or oddly formatted results. Set them per block, per subtree property, per language, or per file:

#+PROPERTY: header-args:python :results value :exports both

Global settings are convenient but harder to audit. Prefer local declarations in shared or security-sensitive documents.

Data flow

A named table can feed a block through :var; a named block can feed a later block; Noweb (:noweb yes) splices named source text into other blocks. Sessions preserve interpreter state and suit exploration, but hidden state weakens reproducibility. Prefer isolated blocks with explicit inputs for final research.

Tangling extracts code with C-c C-v t (org-babel-tangle), writing marked blocks to real files. Documentation then becomes the maintained source of executable files: literate programming, not prose containing copied code. Do not edit tangled output and its source independently; pick one source of truth, and in CI regenerate and fail if the working tree changes. :cache yes skips recomputation when Org judges inputs unchanged, but a cached result can outlive files not represented in its hash.

Security: an Org file can be a program

**Babel asks before evaluating source blocks through org-confirm-babel-evaluate. Do not globally set it to nil merely to remove friction**. A block can delete files, exfiltrate data, or run arbitrary Emacs Lisp with your privileges. Confirmation is one layer, not a sandbox: local variables can request Lisp evaluation, export may run Babel, links may invoke handlers, and tangling writes executable files. Inspect an untrusted Org file as text first, run risky research in a container or low-privilege account, keep credentials out of blocks, and treat generated results as untrusted until validated.

Compared with Jupyter, Org gives transparent text diffs, many languages in one document, and flexible export; Jupyter gives stronger browser collaboration and a familiar widget ecosystem. Both can hide session state. Choose on collaborators and execution needs, not identity.

Summary. Org Babel connects prose, code, data, and results in one plain-text document. Its greatest strength, execution, is also its greatest security risk, so confirmation stays on and reproducibility is designed in.

Exercises

  • Basic: Execute a Python or shell block that prints one line with `C-c C-c`, and note the confirmation prompt Org shows before running it.
  • Practical: Re-run a small analysis from a fresh Emacs and a fresh interpreter, in document order, recording every missing dependency you hit. Then restate why `:results output` and `:results value` differ.
  • Advanced: Threat-model an Org repository from clone through export: list what shell, Emacs Lisp, tangle, export-with-Babel, local variables, and link handlers can each do, then propose least-privilege controls for a CI build.