<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Documentation on colref</title><link>https://shinagawa-web.github.io/colref/docs/</link><description>Recent content in Documentation on colref</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://shinagawa-web.github.io/colref/docs/index.xml" rel="self" type="application/rss+xml"/><item><title>Installation</title><link>https://shinagawa-web.github.io/colref/docs/installation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://shinagawa-web.github.io/colref/docs/installation/</guid><description>&lt;h1 id="installation"&gt;Installation&lt;a class="anchor" href="#installation"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;OS&lt;/th&gt;
 &lt;th&gt;x86_64 (Intel/AMD)&lt;/th&gt;
 &lt;th&gt;arm64&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;macOS&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;✓ (Apple Silicon)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Linux&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Windows&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="pip--pipx-python-users"&gt;pip / pipx (Python users)&lt;a class="anchor" href="#pip--pipx-python-users"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are working on a Django or Python project, install via pipx or pip. No Go installation required.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pipx install colref&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or with pip:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install colref&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="gem-ruby-users"&gt;gem (Ruby users)&lt;a class="anchor" href="#gem-ruby-users"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are working on a Rails or Ruby project, install via gem. No Go installation required.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gem install colref&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="homebrew-macos-and-linux"&gt;Homebrew (macOS and Linux)&lt;a class="anchor" href="#homebrew-macos-and-linux"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install shinagawa-web/tap/colref&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you prefer to tap first:&lt;/p&gt;</description></item><item><title>Getting started</title><link>https://shinagawa-web.github.io/colref/docs/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://shinagawa-web.github.io/colref/docs/getting-started/</guid><description>&lt;h1 id="getting-started"&gt;Getting started&lt;a class="anchor" href="#getting-started"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="django-project"&gt;Django project&lt;a class="anchor" href="#django-project"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following example runs colref against &lt;a href="https://github.com/wagtail/wagtail"&gt;wagtail&lt;/a&gt;, a popular Django CMS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No references found:&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ colref check --orm django --model Page --field search_description
Scanning 932 files...

No references found for Page.search_description

 Verify manually before deleting.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;References found:&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ colref check --orm django --model Page --field seo_title
Scanning 932 files...

References found for Page.seo_title

 wagtail/admin/tests/pages/test_create_page.py:1867 page.seo_title
 wagtail/admin/tests/pages/test_create_page.py:1892 page.seo_title&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="rails-project"&gt;Rails project&lt;a class="anchor" href="#rails-project"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following example runs colref against &lt;a href="https://github.com/mastodon/mastodon"&gt;mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No references found:&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ colref check --orm rails --model Account --field sensitized_at
Scanning 1502 files...

No references found for Account.sensitized_at

 Verify manually before deleting.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;References found:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Use cases</title><link>https://shinagawa-web.github.io/colref/docs/use-cases/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://shinagawa-web.github.io/colref/docs/use-cases/</guid><description>&lt;h1 id="use-cases"&gt;Use cases&lt;a class="anchor" href="#use-cases"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;colref started as a deletion check — &amp;ldquo;is it safe to remove this column?&amp;rdquo; — but the underlying operation (find every reference to a specific field) is useful in more situations. This page documents the ones that come up regularly.&lt;/p&gt;
&lt;h2 id="schema-cleanup"&gt;Schema cleanup&lt;a class="anchor" href="#schema-cleanup"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="dead-column-detection"&gt;Dead column detection&lt;a class="anchor" href="#dead-column-detection"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Instead of starting from a specific deletion candidate, work backwards from the schema. Take a column from your migration history you don&amp;rsquo;t recognize, run colref, and see what comes back.&lt;/p&gt;</description></item><item><title>How it works</title><link>https://shinagawa-web.github.io/colref/docs/how-it-works/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://shinagawa-web.github.io/colref/docs/how-it-works/</guid><description>&lt;h1 id="how-it-works"&gt;How it works&lt;a class="anchor" href="#how-it-works"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Reads your ORM schema source to extract the field list&lt;/li&gt;
&lt;li&gt;Walks the codebase and parses each file into an AST&lt;/li&gt;
&lt;li&gt;Reports every location where the field name appears as an attribute access (e.g. &lt;code&gt;user.email&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;AST parsing avoids false positives from comments, migration files, and unrelated string matches that plain grep would surface.&lt;/p&gt;
&lt;h2 id="schema-extraction"&gt;Schema extraction&lt;a class="anchor" href="#schema-extraction"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Django&lt;/strong&gt; — colref walks the target directory to find &lt;code&gt;models.py&lt;/code&gt; files. Each file is parsed and the field list for the requested model is extracted. If the same model name appears in multiple files, colref exits with an error.&lt;/p&gt;</description></item><item><title>Detection patterns</title><link>https://shinagawa-web.github.io/colref/docs/detection-patterns/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://shinagawa-web.github.io/colref/docs/detection-patterns/</guid><description>&lt;h1 id="detection-patterns"&gt;Detection patterns&lt;a class="anchor" href="#detection-patterns"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;colref uses static AST analysis. It can only detect patterns where the field name appears as a literal in the source. References where the field name is constructed at runtime (e.g. &lt;code&gt;getattr(obj, field_name)&lt;/code&gt;) are out of scope by design — static analysis cannot determine what string &lt;code&gt;field_name&lt;/code&gt; holds at runtime.&lt;/p&gt;
&lt;p&gt;This page documents exactly which patterns are and are not detected for each ORM. The ground truth is the golden test files in &lt;code&gt;test_patterns/&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Limitations</title><link>https://shinagawa-web.github.io/colref/docs/limitations/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://shinagawa-web.github.io/colref/docs/limitations/</guid><description>&lt;h1 id="limitations"&gt;Limitations&lt;a class="anchor" href="#limitations"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;colref uses static AST analysis and cannot detect every reference pattern. References where the field name is constructed at runtime (e.g. &lt;code&gt;getattr(obj, field_name)&lt;/code&gt;) are out of scope by design.&lt;/p&gt;
&lt;blockquote class="book-hint warning" &gt;

**No references found ≠ safe to delete.**
colref reports what static analysis can see. Dynamic references such as `getattr(obj, var)` are out of scope by design — treat zero results as a starting point for human review, not as proof the column is unused.
&lt;/blockquote&gt;
&lt;h2 id="django"&gt;Django&lt;a class="anchor" href="#django"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Detected: attribute access, &lt;code&gt;getattr(obj, &amp;quot;field&amp;quot;)&lt;/code&gt; / &lt;code&gt;attrgetter(&amp;quot;field&amp;quot;)&lt;/code&gt; (labeled &lt;code&gt;[getattr]&lt;/code&gt;), most ORM methods (&lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;exclude&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;Q&lt;/code&gt;, &lt;code&gt;values&lt;/code&gt;, &lt;code&gt;only&lt;/code&gt;, &lt;code&gt;defer&lt;/code&gt;, &lt;code&gt;order_by&lt;/code&gt;, &lt;code&gt;F&lt;/code&gt;, aggregates, etc.), and raw SQL strings.&lt;/p&gt;</description></item><item><title>Contributing</title><link>https://shinagawa-web.github.io/colref/docs/contributing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://shinagawa-web.github.io/colref/docs/contributing/</guid><description>&lt;h1 id="contributing"&gt;Contributing&lt;a class="anchor" href="#contributing"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="setup"&gt;Setup&lt;a class="anchor" href="#setup"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; Go 1.21+&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone https://github.com/shinagawa-web/colref.git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd colref
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make install-hooks&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;make install-hooks&lt;/code&gt; installs a pre-push hook. Every time you run &lt;code&gt;git push&lt;/code&gt;, it automatically runs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Lint&lt;/strong&gt; — golangci-lint&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unit tests + coverage&lt;/strong&gt; — unit tests with 100% coverage enforcement (e2e excluded)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Benchmarks&lt;/strong&gt; — runs benchmarks; compares against &lt;code&gt;main&lt;/code&gt; if &lt;code&gt;benchstat&lt;/code&gt; is installed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s it for most changes. Write code, push, the hook tells you if anything is broken.&lt;/p&gt;
&lt;h2 id="adding-a-new-detection-pattern"&gt;Adding a new detection pattern&lt;a class="anchor" href="#adding-a-new-detection-pattern"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Add the pattern to the relevant scanner in &lt;code&gt;internal/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add test cases to the pattern battery in &lt;code&gt;test_patterns/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make update-golden&lt;/code&gt; to regenerate golden files&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make test-e2e&lt;/code&gt; to verify the pattern battery passes (the pre-push hook does not run e2e)&lt;/li&gt;
&lt;li&gt;Update &lt;a href="https://shinagawa-web.github.io/colref/docs/detection-patterns/"&gt;Detection patterns&lt;/a&gt; with the new pattern&lt;/li&gt;
&lt;li&gt;Push — the hook runs lint, unit tests, and benchmarks&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="submitting-changes"&gt;Submitting changes&lt;a class="anchor" href="#submitting-changes"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Open an issue before starting non-trivial work&lt;/li&gt;
&lt;li&gt;Keep PRs focused — one logical change per PR&lt;/li&gt;
&lt;li&gt;All tests must pass; coverage must stay at 100%&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="running-checks-manually"&gt;Running checks manually&lt;a class="anchor" href="#running-checks-manually"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you need to run individual checks without pushing:&lt;/p&gt;</description></item></channel></rss>