<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Will Poulson</title>
    <description>The latest articles on DEV Community by Will Poulson (@willpoulson).</description>
    <link>https://dev.to/willpoulson</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F736589%2F9affbf60-4c15-4683-8ffc-00b2d545c819.jpeg</url>
      <title>DEV Community: Will Poulson</title>
      <link>https://dev.to/willpoulson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/willpoulson"/>
    <language>en</language>
    <item>
      <title>Most Django Indexes Are Useless. Here’s How to Fix Them.</title>
      <dc:creator>Will Poulson</dc:creator>
      <pubDate>Sun, 04 May 2025 16:19:48 +0000</pubDate>
      <link>https://dev.to/willpoulson/most-django-indexes-are-useless-heres-how-to-fix-them-n86</link>
      <guid>https://dev.to/willpoulson/most-django-indexes-are-useless-heres-how-to-fix-them-n86</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq4k6kg9gavwpc5ef70gq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq4k6kg9gavwpc5ef70gq.png" alt="Most Django Indexes Are Useless. Here’s How to Fix Them." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Most Django developers rely on default indexes (like those on foreign keys), but PostgreSQL (and most RDBMSs) only uses one index per query. If you filter on multiple fields, especially together, a composite index is usually required. Index for how you query, not just how your models look.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What Django Gives You by Default
&lt;/h2&gt;

&lt;p&gt;Django will automatically add indexes for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary keys (the &lt;code&gt;id&lt;/code&gt; field)&lt;/li&gt;
&lt;li&gt;Foreign keys (behind the scenes with &lt;code&gt;db_index=True&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; automatically index:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regular fields you filter on (&lt;code&gt;CharField&lt;/code&gt;, &lt;code&gt;BooleanField&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Fields used in &lt;code&gt;.order_by()&lt;/code&gt; or pagination&lt;/li&gt;
&lt;li&gt;Fields used together in queries (composite indexes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Invoice(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE) # FK = auto-indexed
    created_at = models.DateTimeField()
    status = models.CharField(max_length=20) # Not indexed by default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This model will have indexes on &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;user_id&lt;/code&gt;, but &lt;strong&gt;not&lt;/strong&gt; on &lt;code&gt;status&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The presence of ForeignKey fields and Django’s automatic primary keys can give a false sense of security. It’s easy to assume that if a field is indexed, the query will be fast. But indexing isn’t about whether a field &lt;em&gt;can&lt;/em&gt; be queried — it’s about how fields are &lt;em&gt;used together&lt;/em&gt; in your queries. Relying only on what Django gives you by default often leads to missed performance wins.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The More Field Indexes, the Merrier - Right?
&lt;/h2&gt;

&lt;p&gt;It’s often believed that adding &lt;code&gt;db_index=True&lt;/code&gt; to all fields you might filter on is enough. But this is where things get counterintuitive:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL only uses one index per table scan.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your query filters on multiple fields, the planner won’t merge several single-column indexes. It’ll pick the one it believes is most selective and apply the other filters in memory.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example query:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoice.objects.filter(user_id=1, status="paid")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with indexes on both &lt;code&gt;user_id&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt;, Postgres might still do a sequential scan:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example &lt;code&gt;.explain()&lt;/code&gt; output:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan on invoice (cost=0.00..1204.50 rows=1000 width=48)
  Filter: ((user_id = 1) AND (status = 'paid'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means no index was used effectively. For large tables, this becomes a performance bottleneck.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. How to Index for Composite Queries
&lt;/h2&gt;

&lt;p&gt;You need to design indexes that reflect how your data is used, not just how it’s structured.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fixing the example:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Meta:
    indexes = [
        models.Index(fields=["user", "status"]),
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows Postgres to filter efficiently when both fields are queried together.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Composite indexes are only used when the &lt;strong&gt;leading column(s)&lt;/strong&gt; in the index are part of the query. An index on &lt;code&gt;("user", "status")&lt;/code&gt; will not help if you're only filtering by &lt;code&gt;status&lt;/code&gt;. The query must include &lt;code&gt;user&lt;/code&gt; to make use of the index.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also note: using &lt;code&gt;models.Index(fields=["field"])&lt;/code&gt; provides the same underlying result as &lt;code&gt;db_index=True&lt;/code&gt;, but it can be more readable and easier to evolve, especially if you plan to later modify or combine indexes.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The Trade-Offs
&lt;/h2&gt;

&lt;p&gt;Indexes come with costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They consume disk space&lt;/li&gt;
&lt;li&gt;They slow down writes (&lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;) because each index must also be updated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid blindly creating composite indexes for every possible combination of fields. Instead, focus on what's frequent, slow, or scaling poorly — and back your decisions with &lt;code&gt;.explain()&lt;/code&gt; output and real-world usage.&lt;/p&gt;

&lt;p&gt;Indexing is only effective when it aligns with your actual query patterns. Adding indexes for every field often leads to bloat and diminishing returns.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Reviewing Indexes in Practice
&lt;/h2&gt;

&lt;p&gt;When reviewing model or migration PRs, we ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this field part of a common filter?&lt;/li&gt;
&lt;li&gt;Are these fields used together in filters?&lt;/li&gt;
&lt;li&gt;Are we paginating or sorting by this field?&lt;/li&gt;
&lt;li&gt;Does this need a composite or partial index instead of multiple single-field ones?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common red flag: &lt;code&gt;db_index=True&lt;/code&gt; on three different fields — all used together — but no composite index in sight.&lt;/p&gt;

&lt;p&gt;Good indexing is less about marking every field as "indexable" and more about being precise, intentional, and aware of how queries behave in the real world.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Indexes are one of the most powerful tools in your performance toolbox — but only if they’re actually used.&lt;/p&gt;

&lt;p&gt;Don’t rely on Django’s defaults. Don’t assume that &lt;code&gt;db_index=True&lt;/code&gt; is sufficient. And don’t expect PostgreSQL to combine indexes for you — it won’t.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Index for how your data is &lt;em&gt;used&lt;/em&gt;, not just how it’s &lt;em&gt;structured&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>django</category>
      <category>database</category>
      <category>performance</category>
    </item>
    <item>
      <title>Why We Wrote a Code Review Guide. And What Actually Worked.</title>
      <dc:creator>Will Poulson</dc:creator>
      <pubDate>Sun, 04 May 2025 16:19:29 +0000</pubDate>
      <link>https://dev.to/willpoulson/why-we-wrote-a-code-review-guide-and-what-actually-worked-34dp</link>
      <guid>https://dev.to/willpoulson/why-we-wrote-a-code-review-guide-and-what-actually-worked-34dp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktmmv23v2na1cdq803qg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktmmv23v2na1cdq803qg.png" alt="Why We Wrote a Code Review Guide. And What Actually Worked." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; We created a code review guide to align expectations, improve feedback quality, and make reviews feel collaborative instead of gatekeeping. Here’s what worked for us.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem We Saw
&lt;/h2&gt;

&lt;p&gt;We didn’t set out to write a code review guide to be formal or process-heavy. We wrote it because our reviews were inconsistent, unstructured, and sometimes even unhelpful. Developers weren’t sure what was expected of them when reviewing or being reviewed, and the quality of feedback varied wildly. We needed to align not just on how to review code, but on why we were doing it in the first place.&lt;/p&gt;

&lt;p&gt;As we dug into the problem, we realized the inconsistency wasn’t just about &lt;em&gt;what&lt;/em&gt; was being reviewed- it was also about &lt;em&gt;how&lt;/em&gt; feedback was communicated. The way comments were delivered varied so much that it was often hard to tell the difference between a question, a suggestion, or a required change. As a result, each comment thread required extra clarification before it could be acted on, which slowed everything down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Wrote a Guide
&lt;/h2&gt;

&lt;p&gt;We didn’t just want to solve tactical issues; we wanted to create a shared understanding of what a good review looked like on our team. Without that foundation, even experienced developers were operating with different assumptions.&lt;/p&gt;

&lt;p&gt;We also saw the guide as a tool for &lt;strong&gt;onboarding&lt;/strong&gt; new team members faster, &lt;strong&gt;reducing review friction&lt;/strong&gt; , and building a culture where reviews were collaborative, respectful, and consistent. Instead of relying on tribal knowledge or guesswork, we wanted clear expectations that everyone could reference and evolve together.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s in the Guide
&lt;/h2&gt;

&lt;p&gt;The guide covers both the &lt;strong&gt;philosophy&lt;/strong&gt; and the &lt;strong&gt;practical mechanics&lt;/strong&gt; of doing a good code review on our team.&lt;/p&gt;

&lt;p&gt;We started with the purpose: code reviews are a way to share knowledge, ensure maintainability, and spot architectural issues early- not just to catch typos or enforce style.&lt;/p&gt;

&lt;p&gt;From there, we broke things down into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer responsibilities&lt;/strong&gt; : what to look for (e.g. clarity, structure, test coverage), and what to avoid (nitpicking without context).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author responsibilities&lt;/strong&gt; : how to write a good PR description, how to request feedback, and how to respond to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tone and communication&lt;/strong&gt; : always assume good intent, prefer questions to demands, and don’t let disagreement become personal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turnaround expectations&lt;/strong&gt; : how quickly to review, and when it’s okay to defer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common pitfalls&lt;/strong&gt; : bikeshedding, “drive-by” reviews, and over-indexing on personal preference.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea was to make the process predictable without being rigid and to empower everyone to participate confidently, regardless of experience level.&lt;/p&gt;

&lt;p&gt;The guide itself is a collaborative project. Anyone on the team can propose edits and contribute to it. This approach ensures the document reflects the evolving needs and insights of the team, and continues to improve over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comment Prefixing: The Simple Trick That Changed Everything
&lt;/h2&gt;

&lt;p&gt;To reduce ambiguity in reviews, we introduced a simple but effective prefixing system. Reviewers tag their comments with one of three labels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;REQ&lt;/strong&gt; – A required change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OPT&lt;/strong&gt; – An optional suggestion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QQ&lt;/strong&gt; – A clarifying question.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These prefixes helped reviewers communicate intent clearly and made it easier for authors to prioritize responses. It also improved tone and reduced friction, especially in larger PRs. No tools required—just a habit that stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Changed
&lt;/h2&gt;

&lt;p&gt;The impact was immediate. The overall quality of reviews improved dramatically- feedback became clearer, more actionable, and more consistent. Developers no longer had to guess which comments were blocking and which were suggestions.&lt;/p&gt;

&lt;p&gt;For PR authors, it meant faster, more confident iteration. They could quickly identify what needed to be addressed to move forward and what could be reasonably discussed or even dismissed. Reviews became less about judgment and more about collaboration.&lt;/p&gt;

&lt;p&gt;The shift in tone also made a difference. By clearly framing feedback, discussions stayed focused and respectful. The process felt more like a conversation between peers, not an audit or gatekeeping step. It encouraged thoughtful dialogue and raised the baseline for what we expect from and contribute to every review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advice to Other Teams
&lt;/h2&gt;

&lt;p&gt;Start small and focus on purpose over process. Agree on what a code review is for, not just how to do it. A shared document can go a long way in aligning expectations—and simple habits like prefixing comments can dramatically improve clarity and tone without adding overhead.&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>teampractices</category>
    </item>
  </channel>
</rss>
