How I Take Notes for Research

How I Take Notes for Research

11 Jul 2025

The key principle I follow is this: how I take my notes will evolve over time. I do not stick to any system too dogmatically.

That said, I’ve settled on a system that’s been fairly robust and stable for the past few years. I have tweaked it here and there to make it easier for me to find what I need.

Emacs configuration #

I make heavy use of org-mode and linking. To make linking as ergonomic as possible, I use the following functions and keep them bound in my global keymap so I can access them in any mode:

  • org-store-link

    This will create a link to whatever your cursor is on and store it for later insertion. It will work really hard to get you a link right back to where you were. Sometimes I use this to create a link to a location in source code to come back to later.

  • org-insert-link-global

    This inserts a previously stored link.

  • org-open-at-point-global

    Visit a link—no matter the mode. In org-mode I can simply use org-open-at-point to follow a link, but having this bound globally means I could insert a link inside e.g. a comment in some source code and then visit its location with this function.

  • org-mark-ring-goto

    This is like a history-back button: once you visit a link, use this function to go back to where you were.

I use Denote and the denote-org and denote-sequence extensions for all my notes. Below is a sample of my configuration:

(use-package denote
  :config
  (denote-rename-buffer-mode)
  (put 'denote-file-type 'safe-local-variable 'symbolp)
  :hook
  (dired-mode . denote-dired-mode)
  :custom
  (denote-file-type 'org)
  (denote-directory (expand-file-name "~/where/i/keep/my/notes/")))

(use-package denote-sequence
  :after denote
  :config)

(use-package denote-org
  :after denote
  :custom
  ;; `denote-org-link-to-heading' controls the behavior of
  ;; `org-store-link': setting to id makes it insert an ID in the
  ;; PROPERTIES drawer
  ;;
  ;; (denote-org-store-link-to-heading 'context)
  (denote-org-store-link-to-heading 'id))

(use-package denote-journal
  :after denote
  :custom
  (denote-journal-keyword "labnotes")
  (denote-journal-directory (expand-file-name "~/where/i/keep/my/notes/labnotes"))
  (denote-journal-title-format 'day-date-month-year))

;; Handy menu for Denote
(use-package transient
  :bind
  (:map global-map
        ("M-s-d" . denote-transient))
  :config
  (transient-define-prefix denote-transient ()
    "Denote dispatch"
    [["Note creation (d)"
      ("dd" "new note" denote)
      ("dj" "new or existing journal entry" denote-journal-new-or-existing-entry)
      ("dn" "open or new" denote-open-or-create)
      ("dt" "new specifying date and time" denote-date)
      ("ds" "create in subdirectory " denote-subdirectory)]
     ["Reviewing (r)"
      ("rd" "notes this day" aw/notes-this-day)]
     ["Folgezettel (f)"
      ("fc" "create parent/child/sibling" denote-sequence)
      ("ff" "find parent/child/sibling notes" denote-sequence-find)
      ("fr" "reparent (adopt) current note into another sequence" denote-sequence-reparent)
      ("fp" "find previous sibling" denote-sequence-find-previous-sibling :transient t)
      ("fn" "find next sibling" denote-sequence-find-next-sibling :transient t)]]
    [["Bookkeeping (b)"
      ("br" "prompt and rename" denote-rename-file)
      ("bf" "rename with frontmatter" denote-rename-file-using-front-matter)
      ("bk" "modify keywords" denote-rename-file-keywords)]
     ["Linking (l)"
      ("li" "insert link" denote-link)
      ("lh" "insert link to org heading" denote-org-link-to-heading)
      ("lb" "show backlinks" denote-backlinks)
      ("lg" "visit backlink" denote-find-backlink)
      ("lo" "org backlink block" denote-org-dblock-insert-backlinks)]]
    [["Searching (s)"
      ("sd" "deft" deft)
      ("sn" "consult-notes" consult-notes)
      ("ss" "consult-notes search" consult-notes-search-in-all-notes)]]))

High-level organization #

The highest-level entity in my research is an “epoch”—this is a topic that spans multiple papers. The current epoch I am working on is for choreographic programming. My previous epoch was type tailoring, and the one before that was floating-point work.

Epochs start a new note sequence. These are the “folgezettel” notes that Denote facilities creating. Choreographies is the first epoch that I started doing this with; it’s signature is =1=. The next epoch will get the signature =2=, and so forth.

Each epoch has, as children, notes for ideas related to that epoch, Paper files, and a “daily notes index” file, the sole purpose of which is to serve as the parent for scores of Daily note files.

An epoch note defines a keyword for that epoch and has the following headings:

  • Products

    List of links to any software projects that result as a byproduct of the research.

  • Papers

    List of links to paper files that fall under this epoch.

  • Current task list

    Most tasks live in daily note files, but sometimes I’ll put TODO items here.

  • Idea backlog

    List of off-the-cuff ideas that are too small to fit into a full note, but might go in a note later on.

I also like keeping a dynamic org block to insert backlinks. I do this in epoch and paper files.

Here is a (truncated) example of what an epoch file is:

#+title:      Choreographies
#+date:       [2024-06-26 Wed 14:01]
#+filetags:   :choreography:epoch:
#+identifier: 20240626T140130
#+signature:  1

Epoch keyword: choreography

* Products

 - [[https://github.com/utahplt/chorex][Chorex]] :: An Elixir library for writing choreographies!

* Papers

#+BEGIN: denote-links :regexp "_choreography.*_paper"
- [[denote:20240710T131628][Choreographies through Tailoring]]
#+END:

* Current task list

See [[denote:20240710T131628][Choreographies through Tailoring::Tasks]].

* Idea backlog

* Related notes

 - [[denote:20240731T164732][Elixir Macro System Pain Points]]
 - [[denote:20250531T233009][1=7  Chorex next steps]]

Backlinks:

#+BEGIN: denote-backlinks :sort-by-component identifier :reverse-sort t
- [[denote:20250523T194509][1=1=116  Friday 23 May 2025]]
- [[denote:20250509T104640][1=1=115  Friday  9 May 2025]]
- [[denote:20250502T113930][1=1=114  Friday  2 May 2025]]
- ...
#+END:

Paper files #

Paper files organize everything about a paper. These are children of an epoch. So, the first paper I write about choreographies will have the sequence =1=1=. Headings in a paper file:

  • Primary links

    Include a link to the epoch the paper belongs to, as well as to the project folder where the LaTeX source of the paper lives.

  • Key information

    Conference deadlines, etc.

  • Contributions

    Keep track of what this paper is trying to accomplish.

  • Things to include in paper

    If I come across something that needs to be cited, mentioned, or dealt with, it goes here.

  • Works referenced

    If I find something when I’m not in a state to put it in the bibliography, it goes here.

  • Tasks

    A list of TODO items for the paper.

Daily note files #

I create one of these a day through the denote-journal-new-or-existing-entry function. Then I have some templates that I expand with the tempel package. Here’s what my templates look like:

(labnotes/generic
 "Epoch: " p n n
 "* Goals for today [/]" n n
 "** Startup" q n n
 "* Work log" n n
 "* Next time")

(labnotes/choreography
 "Epoch: [[denote:20240626T140130][Choreographies]]" n n
 "* Goals for today [/]" n n
 "** Startup" q n n
 "* Work log" n n
 "* Next time")

I create the journal entry, expand the template, then fill out the form. I make sure to “adopt” the note as a child of the “daily note index file” for the current epoch. This file is basically empty and just serves to ensure that the sequence space underneath an epoch doesn’t get cluttered with all these day-to-day task notes.

The daily notes are just there to track what I did that day. They are lab notes. If I have something more substantial to say, it goes into its own separate note file, and then I add a link.

Things that don’t quite fit #

Sometimes I’ll have a thought about an epoch that doesn’t fit in my current paper. When that happens, I just create a new child node of the epoch note. I have lots of notes under the sequence =1= that are not paper files.

Task management #

I have some utilities to go through all of my files tagged labnotes or tasks and produce an org-agenda view. Note: generating the agenda takes on the order of several seconds as it looks through a lot of notes. Below is roughly how I do it:

(use-package org
  :init
  (defun aw/set-update-agenda ()
    "Set or update the agenda list.

Useful for when some files that should be on the agenda list are created
after org initialization."
    (interactive)
    ;; important: setq and not setopt
    (setq org-agenda-custom-commands
          '(("n" "Agenda and All Todos"
             ((agenda)
              (todo)))
            ("w" "Work and research"
             ((agenda)
              (tags-todo "+research")
              (tags-todo "+paper")
              (tags-todo "+reading")
              (tags-todo "+school")
              (tags-todo "+homework")
              (tags-todo "+labnotes-homework"))
             ((org-agenda-tag-filter-preset '("-home"))
              (org-agenda-files
               (append (aw/org-files-with-tag denote-directory "labnotes" 60)
                       (aw/org-files-with-tag denote-directory "tasks" 60)
                       org-agenda-files)))))))
  :config
  (setopt org-latex-pdf-process
          '("lualatex -shell-escape -interaction nonstopmode -output-directory %o %f"
            "lualatex -shell-escape -interaction nonstopmode -output-directory %o %f"
            "lualatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

  (setopt org-export-with-smart-quotes t)

  (setopt org-log-done 'time)               ; Instead of `'time`, also try `'note`
  (setopt org-log-into-drawer t)            ; Move log notes into a drawer

  ;; Semantics:
  ;;  - TODO: open task, ready to do
  ;;  - WAITING: Blocked on something; notes should include what the blocker is
  ;;  - STARTED: Task in-progress
  ;;  - DONE: Task complete; no further action
  ;;  - OBSOLETE: Task not complete, won't fix
  ;;  - MOVED: Task moved to different location;
  ;;    this one kept as record; note indicates where moved
  (setopt org-todo-keywords
          '((sequence "TODO(t)" "WAITING(w@/!)" "STARTED(s!)"
                      "|" "DONE(d!)" "OBSOLETE(o@)" "MOVED(m!/@)")))

  (setopt org-done-keywords '("DONE" "OBSOLETE" "MOVED"))

  (aw/set-update-agenda))

(defun aw/org-files-with-tag (dir tag &optional days-past)
  "Return list of files with filetag `tag' in `dir', optionally filtering to `days-past'"
  (let* ((my-buffer (generate-new-buffer "gather_tags"))
         (process
          (make-process
           :name "gather_tags"
           :buffer my-buffer
           :stderr nil
           :command `("rg"
                      "--files-with-matches"
                      ,(format "^#\\+filetags:.*?:%s:" tag)
                      "--"
                      ,(expand-file-name dir)))))
    (while (accept-process-output process)) ; Flush process output to buffer
    (with-current-buffer my-buffer
      (cl-remove-if-not (lambda (filename)
                          (and (file-exists-p filename)
                               (not (string= filename ""))
                               (or (not days-past)
                                   (time-less-p (time-subtract
                                                 (current-time)
                                                 (days-to-time days-past))
                                                (file-attribute-modification-time
                                                 (file-attributes filename))))))
                        (split-string
                         (buffer-substring-no-properties (point-min) (point-max))
                         "\n")))))

The aw/org-files-with-tag uses ripgrep to find files with the desired tags in the #+filetags: portion of a file. Note that my configuration for denote-journal automatically includes the tag labnotes for any journal entry I create.

Paper management #

I use Zotero to manage my papers. I pay for cloud storage so I can sync my library and annotations between my desktop and my iPad. I love using my iPad to mark up papers. (There’s now an Android app for Zotero.)

I use the Citar package to read the BibTeX database Zotero creates with the Better BibTeX file. I use this to quickly insert citation keys. Citar is smart enough to read your LaTeX files, find the \bibliography{...} directive, and read that BibTeX database when you’re working on that file. Citar is most useful when paired with Vertico.

There are ways to write notes about papers via Denote and Citar; however I typically do all my note writing about papers with a stylus on my iPad. Other, bigger notes that are a product of my thoughts go either in my analog notebook or in a regular note file.

Mastodon