diff --git a/README.md b/README.md
index 986928b36..2b49b7255 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@ These can also be invoked from within emacs:
So you want to grok this madness. Here are a few suggestions:
* **[init.example.el](init.example.el)**: a birds eye view of available modules
-* **[modules/README.md](modules/README.md)**: a primer into module structure
+* **[modules/README.org](modules/README.org)**: a primer into module structure
* **[modules/private/hlissner/+bindings.el](modules/private/hlissner/+bindings.el)**:
my custom keybinds.
* **[modules/private/hlissner/+commands.el](modules/private/hlissner/+commands.el)**:
diff --git a/modules/README.md b/modules/README.md
deleted file mode 100644
index a9599d857..000000000
--- a/modules/README.md
+++ /dev/null
@@ -1,88 +0,0 @@
-# Modules
-
-Modules are made up of four parts, **all of which are optional**:
-
-```text
-modules/category/submodule/
-modules/category/submodule/config.el
-modules/category/submodule/packages.el
-modules/category/submodule/autoload.el
-modules/category/submodule/autoload/*.el
-```
-
-## config.el
-
-The main configuration file and the first loaded when the module is activated
-(using `doom!` or `require!`).
-
-## packages.el
-
-How modules inform DOOM what packages to install and where from. These should be
-declarative, pure and idempotent. That means running them directly should have
-no side-effects (besides affecting the variables `doom-modules` and
-`doom-packages`) and whose results should alway be deterministic.
-
-By default, packages are retrieved from ELPA. Otherwise, a MELPA-style recipe
-can determine how to fetch it:
-
-```emacs-lisp
-;; from modules/tools/rotate-text/packages.el
-(package! rotate-text :recipe (:fetcher github :repo "debug-ito/rotate-text.el"))
-```
-
-Other modules' packages.el files can be depended on, through `depends-on!`:
-
-```emacs-lisp
-;; from modules/feature/file-templates/packages.el
-(depends-on! :feature snippets)
-```
-
-## autoload.el OR autoload/*.el
-
-These are scanned by `doom/reload-autoloads`, whose functions are lazily-loaded,
-given that they're marked with an `;;;###autoload` cookie:
-
-```emacs-lisp
-;; from modules/lang/org/autoload/org.el
-;;;###autoload
-(defun +org/toggle-checkbox ()
- (interactive)
- [...])
-
-;; from modules/lang/org/autoload/evil.el
-;;;###autoload (autoload '+org:attach "lang/org/autoload/evil" nil t)
-(evil-define-command +org:attach (&optional uri)
- (interactive "")
- [...])
-```
-
-## Other files
-
-My convention for extra configuration files is a `+` prefix, e.g.
-`modules/feature/version-control/+git.el`. These are **not** automatically
-loaded, and must be loaded manually with `load!` from within `config.el`:
-
-```emacs-lisp
-;; from modules/feature/version-control/config.el
-(load +git)
-```
-
-----
-
-# What modules aren't
-
-Modules loosely take after Spacemacs' notion of layers, but are not intended to
-be interchangeable. Their purpose is _almost_ purely organizational.
-
-Use `featurep!` to check for module availability:
-
-```emacs-lisp
-;; from modules/lang/go/packages.el
-(when (featurep! :completion company)
- (package! company-go))
-
-;; from modules/lang/go/config.el
-(def-package! company-go
- :when (featurep! :completion company)
- [...])
-```
diff --git a/modules/README.org b/modules/README.org
new file mode 100644
index 000000000..f7603e20b
--- /dev/null
+++ b/modules/README.org
@@ -0,0 +1,167 @@
+#+TITLE: DOOM Modules
+
+* Table of Contents :TOC:noexport:
+- [[#introduction][Introduction]]
+- [[#overview][Overview]]
+ - [[#enablingdisabling-modules][Enabling/disabling modules]]
+- [[#the-structure-of-a-module][The structure of a module]]
+ - [[#configel][config.el]]
+ - [[#packagesel][packages.el]]
+ - [[#autoloadel-or-autoloadel][autoload.el OR autoload/*.el]]
+ - [[#additional-files][Additional files]]
+- [[#appendix][Appendix]]
+
+* Introduction
+DOOM is comprised of its core files and then its modules. These modules loosely take after Spacemacs' layers, utilizing a small list of macros to manage and configure plugins and DOOM Emacs.
+
+These macros are:
+
++ Package management
+ + ~(featurep! MODULE SUBMODULE)~: returns =t= if =:module submodule= is activated
+ + ~(load! NAME)~: loads NAME.el, relative to the current file
+ + ~(require! MODULE SUBMODULE &optional RELOAD-P)~: activates a module & loads its config.el
+ + ~(package! NAME &key recipe pin)~: declares a package and where to get it
+ + ~(depends-on! MODULE SUBMODULE)~: loads a module's packages.el
++ Configuration
+ + ~(set! SETTING &rest ARGS)~: safely cross-configure other modules
+ + ~(def-package! NAME &rest PLIST)~: configure a package (wrapper around ~use-package~)
+ + ~(def-setting! SETTING &rest ARGS)~: defines a setting other modules can ~set!~
+
+The TL;DR of this document is:
+
++ Modules are comprised of: =config.el=, =packages.el=, either =autoload.el= or =autoload/*.el=, and =+*.el= files; these are all optional.
++ =config.el= is the only file loaded when a module is activated, and is where you configure the module and its plugins.
++ =packages.el= files inform DOOM what plugins to install and where from, using the ~package!~ macro. This macro accepts a MELPA-style recipe plist to specify a location other than the ELPA for fetching plugins.
++ Use ~set!~ to safely cross-configure modules; ~doom/describe-setting~ can help you discover what settings are available.
++ Packages are deferred by default; add ~:demand t~ to their ~def-package!~ declaration to load them immediately.
+
+* Overview
+These modules are in their ideal load order.
+
++ :feature :: Broad modules that bring essential functionality to Emacs as an editor.
++ :completion :: Swappable completion modules for narrowing down candidate lists quickly.
++ :ui :: Modules that affect the DOOM user interface or experience.
++ :tools :: Small modules that add specific, non-essential functionality to Emacs.
++ :lang :: Modules that bring support for a language or group of languages to Emacs.
++ :app :: Opinionated and heavy modules that totally transform Emacs' UI to serve a specific purpose.
++ :private :: Private configuration modules that are untracked by version control (except for my personal one; use it as a reference).
+
+** Enabling/disabling modules
+Change the ~doom!~ block in your ~init.el~ file to enable/disable modules on startup. You'll need to restart Emacs.
+
+Don't forget to run ~make~ afterwards to ensure that the needed packages are installed (and unneeded ones are uninstalled).
+
+#+begin_quote
+*Remember*: if you've byte-compiled your config, your changes won't take effect
+until you recompile or delete the \*.elc files.
+#+end_quote
+
+* The structure of a module
+Modules are made up of four *optional* parts:
+
++ config.el :: The heart of a module; loaded when the module is activated.
++ packages.el :: Tells DOOM what packages to install and where from. Isn't loaded until package management commands are used.
++ autoload.el (or autoload/*.el) :: Lazily-loaded functions for that module.
++ +*.el :: Additional config files; not automatically loaded.
+
+** config.el
+*config.el* is loaded immediately. It is the only file proactively loaded by the DOOM module system. Additional files must be explicitly loaded using ~load!~.
+
+It should expect dependencies (in =packages.el=) to be installed and available, but shouldn't make assumptions about what modules are activated (use ~featurep!~ for this).
+
+Packages should be configured using ~after!~ or ~def-package!~ (an alias for ~use-package~).
+
+#+BEGIN_SRC emacs-lisp
+;; from modules/completion/company/config.el
+(def-package! company
+ :commands (company-mode global-company-mode company-complete
+ company-complete-common company-manual-begin company-grab-line)
+ :config
+ (setq company-idle-delay nil
+ company-tooltip-limit 10
+ company-dabbrev-downcase nil
+ company-dabbrev-ignore-case nil)
+ [...])
+#+END_SRC
+
++ Packages are *deferred* by default: add ~:demand t~ to ~def-package!~ blocks to load them immediately.
++ Use ~featurep!~ to test DOOM module availability
++ Use ~set!~ to cross-configure modules safely, e.g. company backends:
+
+ #+BEGIN_SRC emacs-lisp
+;; from modules/lang/python/config.el
+(set! :company-backend 'python-mode '(company-anaconda))
+#+END_SRC
+
+** packages.el
+This file isn't loaded until you use DOOM's package management commands.
+
+Evaluating them should be deterministic, idempotent, and without side-effects (besides updating ~doom-modules~ and ~doom-packages~).
+
+Packages are declared with the ~package!~ macro, e.g.
+
+#+BEGIN_SRC emacs-lisp
+;; from modules/lang/org/packages.el
+(package! org-bullets)
+
+;; from modules/tools/rotate-text/packages.el
+(package! rotate-text :recipe (:fetcher github :repo "debug-ito/rotate-text.el"))
+#+END_SRC
+
+The packages.el of another module can loaded with ~depends-on!~:
+
+#+BEGIN_SRC emacs-lisp
+;; from modules/feature/file-templates/packages.el
+(depends-on! :feature snippets)
+#+END_SRC
+
+** autoload.el OR autoload/*.el
+Functions in these files are lazily loaded. ~doom/reload-autoloads~ will scan these and produce an =autoloads.el= file, which tells Emacs where to find these functions.
+
+For example:
+
+#+BEGIN_SRC emacs-lisp
+;; from modules/lang/org/autoload/org.el
+;;;###autoload
+(defun +org/toggle-checkbox ()
+ (interactive)
+ [...])
+
+;; from modules/lang/org/autoload/evil.el
+;;;###autoload (autoload '+org:attach "lang/org/autoload/evil" nil t)
+(evil-define-command +org:attach (&optional uri)
+ (interactive "")
+ [...])
+#+END_SRC
+
+Autoload files named ~evil*.el~ will be ignored if =:feature evil= isn't loaded.
+
+** Additional files
+The only convention is to prefix additional elisp files with a =+=, e.g.
+=modules/feature/version-control/+git.el=.
+
+These are /not/ loaded automatically. Use ~load!~ from ~config.el~ to do so.
+
+#+BEGIN_SRC emacs-lisp
+;; from modules/feature/version-control/config.el
+(load +git)
+#+END_SRC
+
+* Appendix
++ Macros
+ + ~(featurep! CATEGORY MODULE)~
+ + ~(load! NAME)~
+ + ~(package! NAME &key recipe pin)~
+ + ~(require! CATEGORY MODULE &optional RELOAD-P)~
+ + ~(def-package! NAME &rest PLIST)~
+ + ~(set! SETTING &rest ARGS)~
+ + ~(def-setting! NAME ARGLIST &rest BODY)~
++ Commands
+ + ~doom/reload~
+ + ~doom/reload-autoloads~
+ + ~doom/compile~
+ + ~doom/recompile~
+ + ~doom/compile-lite~
+ + ~doom/clean-cache~
+ + ~doom/clean-compiled~
+