Nim makes macros easy to write. That's the problem.
Before reaching for macro, check the cheaper tools. A const is evaluated at compile time by the same VM that runs macros, so a lookup table built by an ordinary proc is just const table = buildTable(). A static: block runs code during compilation and generates nothing. Checks on config, sizes, string formats. Ordinary procs, called early.
Then templates. Plain substitution, no AST walking. Covers most tiny DSL wishes.
What's left for a real macro is syntax the other three can't express. Even then the macro only rearranges nodes. Logic that doesn't need NimNode lives in ordinary procs, and those I can test at runtime like anything else.
Small macro, big proc. Error messages stay readable.