<?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: Micheal Slyderink</title>
    <description>The latest articles on DEV Community by Micheal Slyderink (@micheal_slyderink_810b3f6).</description>
    <link>https://dev.to/micheal_slyderink_810b3f6</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3597178%2F1bff55b1-98f2-4cc4-b2ce-0e74f203399e.png</url>
      <title>DEV Community: Micheal Slyderink</title>
      <link>https://dev.to/micheal_slyderink_810b3f6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/micheal_slyderink_810b3f6"/>
    <language>en</language>
    <item>
      <title>How Much Should a Seed-Stage Founder Pay Themselves? A Python Model Against Runway</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Fri, 06 Nov 2026 13:23:02 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/how-much-should-a-seed-stage-founder-pay-themselves-a-python-model-against-runway-gmi</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/how-much-should-a-seed-stage-founder-pay-themselves-a-python-model-against-runway-gmi</guid>
      <description>&lt;p&gt;&lt;em&gt;Arron Bennett, Founder of Bennett Financials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most seed-stage founders set their founder salary once, on the day the money lands, and then feel guilty about it for two years. Too low and you're making decisions while worrying about rent. Too high and you've quietly spent months of runway that should have gone into the product.&lt;/p&gt;

&lt;p&gt;The honest answer isn't a benchmark. At seed stage, revenue is small, so the question is how many months of runway each salary costs, measured against your hiring plan.&lt;/p&gt;

&lt;p&gt;This post builds that model in plain Python for a hypothetical AI meeting-notes startup that raised $2.5M, has six people, and pays its two founders $60,000 each. Every figure is example math. Tax points are general information, so confirm how they apply to you with your CPA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At seed stage, founder salary is a runway decision. Model it in months, not feelings.&lt;/li&gt;
&lt;li&gt;Include employer payroll taxes. Every $30,000 raise for two founders costs $64,590 a year, not $60,000.&lt;/li&gt;
&lt;li&gt;In this example, $90,000 each keeps 18 months of runway. $120,000 each falls short, unless the hiring plan drops one engineer.&lt;/li&gt;
&lt;li&gt;Decide salary and hiring together, because they spend the same cash.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How much founder salary can a seed-stage startup afford?
&lt;/h2&gt;

&lt;p&gt;Enough to live without money stress, and no more than your runway can carry. Model monthly costs, include employer payroll taxes and your hiring plan, and find the highest founder salary that still leaves your target runway, often 18 months or more. Revisit it when you raise funds or revenue grows.&lt;/p&gt;

&lt;p&gt;Benchmarks by revenue are useful later. A company with $5 million of revenue can pay its CEO from profit. A seed-stage company with $6,000 of MRR pays its founders from the money investors gave it to build, so cash runway is the constraint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What goes into a founder salary model?
&lt;/h2&gt;

&lt;p&gt;Cash in the bank, current payroll, planned hires and their start month, benefits per person, other monthly costs, current revenue and a growth rate. Add employer payroll taxes on every salary. Then step month by month until cash runs out, once for each founder salary you're considering.&lt;/p&gt;

&lt;p&gt;Employer payroll taxes are the piece people forget. If the founders are on payroll, which they usually are in a C corporation, the company pays 6.2% Social Security and 1.45% Medicare on top of each salary. &lt;a href="https://www.irs.gov/publications/p15" rel="noopener noreferrer"&gt;IRS Publication 15&lt;/a&gt; sets the 2026 Social Security wage base at $184,500. Everyone in this example is under it, so 7.65% applies to all pay. State unemployment taxes are left out to keep it simple.&lt;/p&gt;

&lt;p&gt;The model uses only the standard library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;CASH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2_180_000&lt;/span&gt;           &lt;span class="c1"&gt;# in the bank today, after the $2.5M seed
&lt;/span&gt;&lt;span class="n"&gt;TEAM_SALARIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;520_000&lt;/span&gt;    &lt;span class="c1"&gt;# four employees today, per year
&lt;/span&gt;&lt;span class="n"&gt;HIRES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150_000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;  &lt;span class="c1"&gt;# hiring plan: (start month, salary)
&lt;/span&gt;&lt;span class="n"&gt;OTHER_COSTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;36_000&lt;/span&gt;       &lt;span class="c1"&gt;# per month: inference, cloud, tools, rent
&lt;/span&gt;&lt;span class="n"&gt;BENEFITS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;650&lt;/span&gt;             &lt;span class="c1"&gt;# per person per month (health cover)
&lt;/span&gt;&lt;span class="n"&gt;MRR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GROWTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.04&lt;/span&gt;  &lt;span class="c1"&gt;# revenue today and monthly growth
&lt;/span&gt;&lt;span class="n"&gt;EMPLOYER_FICA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0765&lt;/span&gt;     &lt;span class="c1"&gt;# 6.2% social security + 1.45% Medicare
&lt;/span&gt;&lt;span class="n"&gt;TARGET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;                &lt;span class="c1"&gt;# months of runway you want from today
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;runway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;founder_salary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;founders&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;horizon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CASH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MRR&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;hired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;HIRES&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;salaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TEAM_SALARIES&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;founders&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;founder_salary&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hired&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;heads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;founders&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hired&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;costs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;salaries&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;EMPLOYER_FICA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;BENEFITS&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;heads&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;OTHER_COSTS&lt;/span&gt;
        &lt;span class="n"&gt;cash&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;costs&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cash&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;GROWTH&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;horizon&lt;/span&gt;


&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;runway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;salary each&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;runway&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vs $60K&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  target &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TARGET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;months&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;runway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;months&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;TARGET&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SHORT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;months&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;months&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;extra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;30_000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;EMPLOYER_FICA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+$30K each for two founders: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; a year incl. employer FICA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;HIRES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HIRES&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;one hire instead of two, $120K each: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;runway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; months&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; salary each  runway  vs $60K  target 18
      60,000      19        0  ok
      90,000      18       -1  ok
     120,000      17       -2  SHORT
     150,000      16       -3  SHORT
+$30K each for two founders: 64,590 a year incl. employer FICA
one hire instead of two, $120K each: 19 months
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Runway is the number of full months before cash goes below zero, counting from today. Every figure is pre-tax company cash. Founders' personal income tax comes out of their own pay, not the company's runway.&lt;/p&gt;

&lt;p&gt;The constants are where the arguments belong. The growth rate is your revenue forecast in one number, so test it. Set GROWTH to zero and run it again before any board reporting, because that's the scenario planning investors will do in their heads anyway. If the salary you want only works at 4% monthly growth, it isn't affordable yet. It's a bet on the forecast.&lt;/p&gt;

&lt;p&gt;This short gives CEO salary ranges by revenue, from $1 million up to $20 million. It's the later-stage view. At seed, before revenue, the model above is how I'd decide instead.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/jNQ8cuQt7o4" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the model say about this startup?
&lt;/h2&gt;

&lt;p&gt;At $60,000 each, the company has 19 months. Raising both founders to $90,000 costs one month and still meets the 18-month target. At $120,000 each, runway drops to 17 months, and at $150,000 to 16. The hiring plan does more damage than salary: two engineers starting in month four.&lt;/p&gt;

&lt;p&gt;So the honest options are clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$90,000 each, with both hires. 18 months of runway, on target.&lt;/li&gt;
&lt;li&gt;$120,000 each, with one hire. 19 months, but slower product work.&lt;/li&gt;
&lt;li&gt;$60,000 each, with both hires. 19 months and two tired founders.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is the right answer for every company. The point is that the founders and the board can see the trade in months. A salary that keeps a founder focused can be worth one month of runway. It's not worth three.&lt;/p&gt;

&lt;p&gt;I'd usually pick the first option. Founders on $60,000 in an expensive city can end up taking side work or making short-term decisions to fix personal cash, and that costs more than a month.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should founders revisit their salary?
&lt;/h2&gt;

&lt;p&gt;At every round and every time the financial projections change materially. A new raise resets runway and usually justifies a step up. Revenue growth, a lost hire or a cost cut also changes the answer. Re-run the model, agree the result with the board, and write down the trigger for the next review.&lt;/p&gt;

&lt;p&gt;A few practical rules I'd add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep founder salaries in the same model as hiring plans, so the trade-off is always visible.&lt;/li&gt;
&lt;li&gt;Show potential investors the model. Reasonable founder pay rarely worries them. Unexplained pay does.&lt;/li&gt;
&lt;li&gt;Check the result against the 18-month line in a downside scenario too, for example zero revenue growth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's more on the early finance decisions in this piece on &lt;a href="https://bennettfinancials.com/cfo-guidance-for-startups" rel="noopener noreferrer"&gt;CFO guidance for startups&lt;/a&gt;, and how founder pay sits alongside the cap table in &lt;a href="https://bennettfinancials.com/saas-founder-tax-strategy-cap-table-and-financial-statement-essentials" rel="noopener noreferrer"&gt;SaaS founder tax strategy&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do on Monday?
&lt;/h2&gt;

&lt;p&gt;Copy the script and replace the constants with your bank balance, payroll, hiring plan and revenue. Run it with the salaries you're considering. Take the output to your co-founder and board, pick a salary that clears your runway target, and set the date you'll review it.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should co-founders be paid the same?
&lt;/h3&gt;

&lt;p&gt;Usually, at seed stage, if they're full time and equally committed. Unequal pay is a conversation to have early and in writing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should seed-stage founders take a salary at all?
&lt;/h3&gt;

&lt;p&gt;Yes, once you've raised. A founder under money stress makes worse decisions, and payroll keeps your records clean.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a founder salary taxed differently from other wages?
&lt;/h3&gt;

&lt;p&gt;Not usually. A founder on payroll in a C corporation is paid ordinary wages, with income tax withheld and Social Security and Medicare paid on both sides. How salary and equity interact for you is a question for your CPA.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do founder salaries count toward employer payroll taxes?
&lt;/h3&gt;

&lt;p&gt;Yes, if they're paid as wages. The company pays 6.2% Social Security, up to the wage base, and 1.45% Medicare on top of each salary.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>startup</category>
    </item>
    <item>
      <title>Backlog Forecasting for Engineering Firms: A Python Script That Turns Signed Work Into a Revenue Curve</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Thu, 05 Nov 2026 11:15:16 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/backlog-forecasting-for-engineering-firms-a-python-script-that-turns-signed-work-into-a-revenue-59kf</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/backlog-forecasting-for-engineering-firms-a-python-script-that-turns-signed-work-into-a-revenue-59kf</guid>
      <description>&lt;p&gt;&lt;em&gt;Arron Bennett, Founder of Bennett Financials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ask the owner of an engineering firm how next year looks and you'll usually hear one number: the backlog. "We've got $5.3M signed." It sounds safe. It tells you almost nothing about when that money turns into revenue, and timing is the whole question.&lt;/p&gt;

&lt;p&gt;Backlog forecasting fixes that. You take every signed project, spread its remaining fee over the months the work will actually happen, and add the months up. The result is a revenue curve, and the curve usually shows a cliff that the single number hides.&lt;/p&gt;

&lt;p&gt;This post builds that curve in plain Python for a hypothetical 44-person MEP engineering firm: $7.1M of revenue last year and $5.3M of signed backlog. Every figure is example math. Swap in your own projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A backlog total says how much work is signed. A burn schedule says when it becomes revenue. You need both.&lt;/li&gt;
&lt;li&gt;In this example, $5.3M of backlog is nine months of revenue, yet it covers only 43% of each month from July onward.&lt;/li&gt;
&lt;li&gt;The gap to fill over the next 12 months is about $2.04M, which at a 35% win rate means roughly $5.83M of proposals.&lt;/li&gt;
&lt;li&gt;Track book to bill monthly. Under 1.0 for a year is how a cliff like this gets built.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is backlog forecasting?
&lt;/h2&gt;

&lt;p&gt;Backlog forecasting turns signed but unearned fees into a month-by-month revenue forecast. For each project you set the remaining fee, the start month and how long the work will take, then spread the fee across those months. Summing every project by month shows how much of each future month is already covered and how much still has to be won.&lt;/p&gt;

&lt;p&gt;The total backlog is a stock. Revenue is a flow. A firm with $5.3M signed can still have a thin summer if most of that money sits in one long hospital job and a handful of projects that finish by spring. That's why I'd rather see the curve than the total.&lt;/p&gt;

&lt;p&gt;For project-based firms, the curve is also the best early warning you have. The P&amp;amp;L tells you what happened last month. The backlog curve tells you what will happen in month seven, while there's still time to do something about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why isn't the backlog total enough?
&lt;/h2&gt;

&lt;p&gt;Because it hides timing. "Nine months of backlog" sounds like nine full months of work. In practice, some projects burn fast and finish early, others stretch past the year, and new starts slip. The total stays the same while coverage of specific months collapses. You find out when utilization drops and the team is already on payroll.&lt;/p&gt;

&lt;p&gt;It also hides the trend. A backlog that held at $5.3M because you won a lot is different from one that held because projects slowed down. Book to bill, new signed work divided by revenue for the same period, tells you which. Here, say the firm signed $6.4M of new work over the last 12 months against $7.1M of revenue. That's a book to bill ratio of about 0.90. Backlog fell by $700,000 in the year, from about $6.0M to $5.3M.&lt;/p&gt;

&lt;p&gt;There's a second source of work the backlog never shows: clients who come back. In this short I talk about repeat rate, how often a client comes back in a year, as the number that lets you predict next year's revenue. For an engineering firm, that's the repeat-client side of this forecast.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/OazKYdkwp44" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the Python look like?
&lt;/h2&gt;

&lt;p&gt;One list of signed projects with four fields: name, remaining fee, start month and months to finish. The script spreads each fee evenly across its months, sums the months, and compares each month with last year's monthly revenue. It prints coverage, the monthly gap, the 12-month total and the proposals you'd need at your win rate.&lt;/p&gt;

&lt;p&gt;Even burn is a simplification. Real projects ramp up, peak through design development and taper during construction administration. If your project management tool exports planned hours by month, use those instead. The structure doesn't change. Standard library only, and you can paste it and run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;BACKLOG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  &lt;span class="c1"&gt;# signed work: (project, remaining fee $, start month from now, months to finish)
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hospital wing MEP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_450_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Office tower retrofit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;880_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;K-12 school renovation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;620_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Senior living campus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;560_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Lab fit-out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;540_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Data center cooling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;410_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Municipal library&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hotel renovation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;On-call small jobs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;240_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;MONTHS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Nov&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Feb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Apr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;May&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jun&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jul&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Aug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Oct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;RUN_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7_100_000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;          &lt;span class="c1"&gt;# last year's revenue, per month
&lt;/span&gt;&lt;span class="n"&gt;WIN_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.35&lt;/span&gt;                    &lt;span class="c1"&gt;# share of proposed fees you win, from your own history
&lt;/span&gt;
&lt;span class="n"&gt;curve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;months&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BACKLOG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;months&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;curve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;fee&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;months&lt;/span&gt;   &lt;span class="c1"&gt;# even burn schedule; swap in your own
&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signed backlog $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BACKLOG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; = &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BACKLOG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;RUN_RATE&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; months of revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;month&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;from backlog&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coverage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gap&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gap_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MONTHS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RUN_RATE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;curve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gap_total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;curve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;curve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;RUN_RATE&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curve&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;next 12 months from backlog $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;7_100_000&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; of last year)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gap to fill $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gap_total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;; proposals needed at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;WIN_RATE&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; win rate $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gap_total&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;WIN_RATE&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;backlog burning after month 12: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;signed backlog $5,300,000 = 9.0 months of revenue
month   from backlog  coverage        gap
Nov          496,405       84%     95,262
Dec          546,405       92%     45,262
Jan          602,405      102%          0
Feb          602,405      102%          0
Mar          602,405      102%          0
Apr          520,405       88%     71,262
May          380,405       64%    211,262
Jun          330,405       56%    261,262
Jul          252,905       43%    338,762
Aug          252,905       43%    338,762
Sep          252,905       43%    338,762
Oct          252,905       43%    338,762
next 12 months from backlog $5,092,857 (72% of last year)
gap to fill $2,039,357; proposals needed at 35% win rate $5,826,735
backlog burning after month 12: $207,143
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What does the revenue curve say about this firm?
&lt;/h2&gt;

&lt;p&gt;The firm is full through spring and exposed after it. January to March run at 102% of last year's monthly revenue, so the team is stretched. From May, coverage falls fast, and July to October sit at 43%. The backlog total looked comfortable. The curve shows a summer cliff that new wins have to fill.&lt;/p&gt;

&lt;p&gt;Three numbers matter here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$5,092,857 of the next 12 months is already signed&lt;/strong&gt;, about 72% of last year's revenue. The rest, $207,143, burns after month 12, mostly the tail of the hospital job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The gap is $2,039,357.&lt;/strong&gt; That's the revenue that has to come from work not yet signed, before you even think about growth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;At a 35% win rate, that's roughly $5.83M of proposals.&lt;/strong&gt; Treat it as a floor. Work you win in June won't all burn by October, so the real pipeline you need is bigger.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The over-coverage in January to March is a signal too. It means overtime, outside help or late deliverables, and a reason to stagger new starts instead of saying yes to every one at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you use the forecast to make decisions?
&lt;/h2&gt;

&lt;p&gt;Use the coverage line to time hiring, pursuit effort and cash. If coverage six months out is under about 70%, pursuit becomes the priority now, not in month five. If coverage is over 100%, slow new starts or plan the capacity. Review it monthly alongside book to bill and win rate, the key performance indicators that move the curve.&lt;/p&gt;

&lt;p&gt;A few rules I'd keep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update the start months honestly. Slipped notices to proceed are the most common reason a backlog forecast flatters you.&lt;/li&gt;
&lt;li&gt;Keep a separate line for repeat clients. If, say, 60% of last year's $6.4M of signed work came from clients who'd hired you before, that's about $3.84M of next year's pipeline to protect with relationships, not proposals.&lt;/li&gt;
&lt;li&gt;Don't hire against the January peak. Hire against the coverage six to nine months out.&lt;/li&gt;
&lt;li&gt;Watch cash alongside revenue. Coverage tells you what you'll bill, and your collection days tell you when it turns into operating cash flow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've written a longer guide to &lt;a href="https://bennettfinancials.com/backlog-forecasting-project-based-firms" rel="noopener noreferrer"&gt;backlog forecasting for project-based firms&lt;/a&gt;, and one on reading the &lt;a href="https://bennettfinancials.com/book-to-bill-ratio-service-business" rel="noopener noreferrer"&gt;book-to-bill ratio&lt;/a&gt; in a service business.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do on Monday?
&lt;/h2&gt;

&lt;p&gt;Export your signed projects with remaining fee, expected start and duration. Paste them into the script, set your own run rate and win rate, and run it. Then look at coverage six months out. If it's under 70%, put pursuit on this week's agenda, and agree who owns which proposals.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a good book to bill ratio for an engineering firm?
&lt;/h3&gt;

&lt;p&gt;Above 1.0 means backlog is growing, below 1.0 means it's shrinking. A single quarter below 1.0 isn't a crisis. A full year below it, as in this example, is how a revenue cliff gets built.&lt;/p&gt;

&lt;h3&gt;
  
  
  How many months of backlog should a firm have?
&lt;/h3&gt;

&lt;p&gt;It depends on project length. Months of backlog is a useful headline, but coverage by month is what tells you whether next summer is safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should backlog forecasting use fees or hours?
&lt;/h3&gt;

&lt;p&gt;Fees for the revenue curve, hours for capacity. If your tools hold planned hours by month, convert them to fee and use that shape instead of an even burn.&lt;/p&gt;

</description>
      <category>data</category>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>Marketing Attribution for Small SaaS Teams: Tie Signups to Revenue With Python</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Wed, 04 Nov 2026 13:30:45 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/marketing-attribution-for-small-saas-teams-tie-signups-to-revenue-with-python-2klh</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/marketing-attribution-for-small-saas-teams-tie-signups-to-revenue-with-python-2klh</guid>
      <description>&lt;p&gt;&lt;em&gt;Arron Bennett, Founder of Bennett Financials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most small SaaS teams can tell you which channel brings the most signups. Very few can tell you which channel brings the most revenue. That gap is what marketing attribution is for, and you don't need an attribution platform to close it. You need two exports and a join.&lt;/p&gt;

&lt;p&gt;Signups are easy to count, so they end up running the budget. But a signup isn't revenue. Some channels bring lots of people who never pay. Others bring a few who pay a lot.&lt;/p&gt;

&lt;p&gt;This post builds a first-touch attribution report in pandas for a hypothetical bootstrapped job board for travel nurses, where staffing agencies pay a monthly subscription to post jobs. It's at $39,000 of MRR, with four acquisition channels and no attribution at all. Every figure is example data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tag every signup with its first-touch UTM source and store it on the account, not just in your analytics tool.&lt;/li&gt;
&lt;li&gt;Join signups to paid invoices by account, and judge channels on revenue in a fixed window, not on signups.&lt;/li&gt;
&lt;li&gt;In this example, Google Ads brings the most signups and the least revenue per dollar spent. A newsletter sponsorship brings the fewest signups and the most.&lt;/li&gt;
&lt;li&gt;Count untagged signups. If a tenth of your accounts have no source, fix tracking before you move budget.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is marketing attribution for a small SaaS team?
&lt;/h2&gt;

&lt;p&gt;Marketing attribution is working out which acquisition channel gets the credit for each customer, so you can compare channels on what they bring in. For a small team, first-touch attribution is enough to start: record the UTM source on the first visit that led to a signup, keep it on the account, and later join it to revenue.&lt;/p&gt;

&lt;p&gt;There are fancier models: last touch, linear, time decay. They answer questions a team at $39,000 of MRR usually isn't asking yet. The question here is simpler: of the money we spend on each channel, which one comes back as paying customers?&lt;/p&gt;

&lt;p&gt;UTM parameters are the plumbing. Google's guide to &lt;a href="https://support.google.com/analytics/answer/10917952" rel="noopener noreferrer"&gt;campaign URLs&lt;/a&gt; says to always set utm_source, utm_medium and utm_campaign. For this report, utm_source is the one that matters, and it has to land in your database, not just in an analytics dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you capture the first-touch UTM source?
&lt;/h2&gt;

&lt;p&gt;Read the UTM parameters when a visitor first lands, store them in a first-party cookie or local storage, and write them onto the account record when they sign up. Don't overwrite them on later visits. Then every account carries its first-touch source, and you can join it to billing data whenever you like.&lt;/p&gt;

&lt;p&gt;A few rules that save a lot of cleanup later:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use one spelling per channel. "linkedin", "LinkedIn" and "li" will show up as three channels.&lt;/li&gt;
&lt;li&gt;Tag every link you control: ads, sponsorships, emails, social posts.&lt;/li&gt;
&lt;li&gt;Leave untagged signups as blank, not "direct". You want to see how many you're missing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This short isn't about UTM tags. It's about diagnosing marketing with the wrong numbers: if you judge cost per acquisition and lifetime value before pricing and cost of revenue are settled, every marketing metric moves under you. Attribution has the same trap. Judge channels on signups and you're reading the wrong number.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/t6IBgzhR5Cs" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you tie signups to revenue in pandas?
&lt;/h2&gt;

&lt;p&gt;Export signups with their account ID, signup date and first-touch source. Export paid invoices with account ID, paid date and amount. Merge them on account ID, keep only invoices paid within 90 days of signup, and group by source. Add each channel's spend for the same period, and you can compare channels on revenue, not signups.&lt;/p&gt;

&lt;p&gt;The 90-day window matters. Without it, older channels look better simply because their customers have had longer to pay. With it, every channel gets the same time to prove itself.&lt;/p&gt;

&lt;p&gt;The sample data is inline so you can paste it and run it. You'll need pandas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;signups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;account_id,signup_date,utm_source
a101,2026-07-03,google_ads
a102,2026-07-08,linkedin
a103,2026-07-11,google_ads
a104,2026-07-15,newsletter
a105,2026-07-19,google_ads
a106,2026-07-22,seo
a107,2026-07-28,
a108,2026-08-02,google_ads
a109,2026-08-05,seo
a110,2026-08-09,linkedin
a111,2026-08-14,google_ads
a112,2026-08-18,newsletter
a113,2026-08-21,linkedin
a114,2026-08-27,google_ads
a115,2026-08-30,seo
a116,2026-09-02,
a117,2026-09-05,google_ads
a118,2026-09-09,newsletter
a119,2026-09-12,linkedin
a120,2026-09-16,google_ads
a121,2026-09-20,seo
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;parse_dates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signup_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;account_id,paid_date,amount
a103,2026-07-25,149
a104,2026-07-29,499
a103,2026-08-25,149
a104,2026-08-29,499
a106,2026-08-05,299
a107,2026-08-11,299
a110,2026-08-23,299
a103,2026-09-25,149
a104,2026-09-29,499
a106,2026-09-05,299
a107,2026-09-11,299
a110,2026-09-23,299
a114,2026-09-10,149
a115,2026-09-13,149
a118,2026-09-23,299
a104,2026-10-29,499
a106,2026-10-05,299
a107,2026-10-11,299
a110,2026-10-23,299
a114,2026-10-10,149
a115,2026-10-13,149
a118,2026-10-23,299
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;parse_dates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paid_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;spend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google_ads&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linkedin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;newsletter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# Jul-Sep
&lt;/span&gt;
&lt;span class="n"&gt;signups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utm_source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;signups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utm_source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(no utm)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;paid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;account_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;paid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paid_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signup_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;signups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utm_source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;to_frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signups&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paying&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utm_source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;account_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;nunique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utm_source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sort_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ascending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;signups&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;paying&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rev_90d&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;spend&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cost/payer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rev/$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterrows&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cpp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paying&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paying&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;rps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;revenue&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signups&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paying&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;revenue&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;cpp&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;untagged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utm_source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(no utm)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;total: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; signups, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;account_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;nunique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; paying, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;paid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in first-90-day revenue, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;untagged&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; of signups untagged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;channel      signups  paying  rev_90d   spend  cost/payer  rev/$
newsletter         3       2    2,095   1,800         900   1.16
seo                4       2    1,195   1,200         600   1.00
(no utm)           2       1      897       0           -      -
linkedin           4       1      897   2,700       2,700   0.33
google_ads         8       2      745   4,500       2,250   0.17

total: 21 signups, 8 paying, 5,829 in first-90-day revenue, 10% of signups untagged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Revenue here is invoices paid in each account's first 90 days. Cost per payer is the channel's spend for the quarter divided by the accounts from that channel that paid at least once. Notice one invoice is missing from the totals: an account's fourth payment landed on day 106, outside the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the attribution report say about this business?
&lt;/h2&gt;

&lt;p&gt;Google Ads brings the most signups and the least revenue for its spend. It produced 8 of the 21 signups but only $745 of first-90-day revenue on $4,500 of spend, 17 cents per dollar. The newsletter sponsorship produced 3 signups and $2,095 on $1,800, $1.16 per dollar. Ranked by signups, the budget runs backwards.&lt;/p&gt;

&lt;p&gt;The detail behind it is common. Google Ads pulls in individual recruiters on the cheapest plan, and many never post a job. The newsletter reaches agency owners who buy the bigger plans. SEO sits in the middle: $1,195 on $1,200 of content spend, and that content keeps working next quarter.&lt;/p&gt;

&lt;p&gt;Two more things jump out. LinkedIn costs $2,700 per paying account, the highest of the four. And 10% of signups have no UTM source at all, one of which became an $897 customer. Until that bucket shrinks, every channel's numbers are understated.&lt;/p&gt;

&lt;p&gt;In total, the quarter's $10,200 of spend is about $3,400 a month, about 8.7% of MRR, and it brought 8 paying accounts. If you want context on how much to spend in the first place, I've collected &lt;a href="https://bennettfinancials.com/research/marketing-spend" rel="noopener noreferrer"&gt;research on marketing spend&lt;/a&gt; as a share of revenue.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you change in the budget?
&lt;/h2&gt;

&lt;p&gt;Move money slowly, toward the channels that bring revenue per dollar, and test before you cut. Shift part of the Google Ads budget to a second newsletter sponsorship for one quarter, keep SEO going, and fix the untagged signups. Then rerun the same report on the next quarter's cohort before moving more.&lt;/p&gt;

&lt;p&gt;A few cautions before you act on a report like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small samples lie.&lt;/strong&gt; A handful of paying accounts per channel is a signal to test, not proof. Run it every quarter and look for the pattern holding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First touch isn't the whole story.&lt;/strong&gt; A customer might find you on Google and decide after a newsletter. First touch credits Google. That's fine for a start; just know it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revenue isn't profit.&lt;/strong&gt; If one channel brings customers with much higher hosting or support costs, look at gross margin at the company level too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch retention by channel.&lt;/strong&gt; Join the same table to cancellations later, and you'll see which channels bring customers who stay.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What should you do on Monday?
&lt;/h2&gt;

&lt;p&gt;Check that every signup in your database has a first-touch source field. If it doesn't, add one this week and start capturing it. Then export last quarter's signups and paid invoices, paste them into the script, add each channel's spend, and read the table. Write down one budget test for next quarter.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is first-touch attribution?
&lt;/h3&gt;

&lt;p&gt;It gives full credit for a customer to the first channel that brought them to your site. It's simple, stable and good enough for a small team comparing a few channels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need an attribution tool for this?
&lt;/h3&gt;

&lt;p&gt;Not at the start. If you store the UTM source on the account and can export invoices, a short script does the job. Tools help once you have many channels and campaigns.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long should the revenue window be?
&lt;/h3&gt;

&lt;p&gt;Long enough for a typical customer to pay a few times. Ninety days works for monthly plans. Use the same window for every channel so the comparison is fair.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>marketing</category>
      <category>python</category>
      <category>saas</category>
    </item>
    <item>
      <title>Engineering Headcount Planning: A Small Python Model for When You Can Afford the Next Engineer</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Tue, 03 Nov 2026 15:08:03 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/engineering-headcount-planning-a-small-python-model-for-when-you-can-afford-the-next-engineer-2994</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/engineering-headcount-planning-a-small-python-model-for-when-you-can-afford-the-next-engineer-2994</guid>
      <description>&lt;p&gt;&lt;em&gt;Arron Bennett, Founder of Bennett Financials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At most bootstrapped companies, engineering headcount planning happens in the founder's head. Revenue is growing, the backlog is growing faster, so you hire. A few months later the bank balance is lower than you expected and you're not sure which decision did it.&lt;/p&gt;

&lt;p&gt;A spreadsheet fixes that. So do about 30 lines of Python, which some technical founders will actually keep up to date. This post walks through a small month-by-month cash model for deciding when the next engineer is affordable, using a hypothetical bootstrapped transactional email API at $118,000 of MRR with nine people, planning its 2027 hires.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plan hires against monthly cash, not annual profit. The start date of each hire changes the answer.&lt;/li&gt;
&lt;li&gt;Use fully loaded cost: salary plus payroll taxes, benefits and equipment. Add a one-time hiring cost in the start month.&lt;/li&gt;
&lt;li&gt;Set a cash floor, such as three months of total costs, and treat any month below it as a no.&lt;/li&gt;
&lt;li&gt;In this example, one engineer in February is comfortable, a second in June dips under the floor in November, and the same second hire in October doesn't.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is headcount planning for a bootstrapped SaaS?
&lt;/h2&gt;

&lt;p&gt;Headcount planning is deciding who you'll hire, and when, based on what the business can afford month by month. For a bootstrapped SaaS without investor cash behind it, the binding constraint is the bank balance, not the budget. A good plan shows the lowest cash month for each set of hiring plans before anyone signs an offer.&lt;/p&gt;

&lt;p&gt;Annual numbers hide the problem. A hire can look fine across the year and still push one month under the line you've promised yourself. That's why the model below runs month by month.&lt;/p&gt;

&lt;h2&gt;
  
  
  What inputs does an engineering headcount plan need?
&lt;/h2&gt;

&lt;p&gt;Seven numbers: starting cash, current MRR, monthly growth, variable costs as a share of revenue, today's fixed costs, the fully loaded monthly cost of an engineer, and a one-time hiring cost. Add a cash floor, the minimum you'll let the bank balance fall to. Everything else is a scenario: which start month for which hire.&lt;/p&gt;

&lt;p&gt;For the email API, all hypothetical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting cash on January 1, 2027: $420,000.&lt;/li&gt;
&lt;li&gt;MRR: $118,000, growing 1.5% a month.&lt;/li&gt;
&lt;li&gt;Variable costs, meaning sending infrastructure, payment fees and support tools: 20% of MRR.&lt;/li&gt;
&lt;li&gt;Fixed costs for today's nine people, tools and rent: $74,000 a month.&lt;/li&gt;
&lt;li&gt;One engineer, fully loaded: $17,500 a month, or $210,000 a year, covering base salary, employer payroll taxes, benefits and equipment.&lt;/li&gt;
&lt;li&gt;One-time hiring cost per engineer: $25,000, in the start month.&lt;/li&gt;
&lt;li&gt;Cash floor: three months of total costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In January the business makes about $20,400 of pre-tax operating profit: $118,000 of MRR, less $23,600 of variable costs and $74,000 of fixed costs. That's the cushion every hire draws on.&lt;/p&gt;

&lt;p&gt;The model is pre-tax. Taxes matter, and the 2025 tax law changed them for software teams: domestic development costs can again be deducted in the year you pay them, while offshore development still amortizes over 15 years, per the &lt;a href="https://www.irs.gov/newsroom/one-big-beautiful-bill-provisions" rel="noopener noreferrer"&gt;IRS summary of the law&lt;/a&gt;. I've written about how that changes a &lt;a href="https://bennettfinancials.com/section-174a-domestic-offshore-development-budget" rel="noopener noreferrer"&gt;domestic vs offshore development budget&lt;/a&gt;. Add a tax line once your accountant has given you an estimate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the Python model look like?
&lt;/h2&gt;

&lt;p&gt;It's one function that walks through twelve months. Each month it grows MRR, adds the cost of every engineer who has started, takes the hiring cost in the start month, and updates cash. It also tracks headroom, meaning cash above the floor, and reports the tightest month. It's plain Python with no libraries, so paste it and run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;START_CASH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;420_000&lt;/span&gt;   &lt;span class="c1"&gt;# cash on Jan 1, 2027
&lt;/span&gt;&lt;span class="n"&gt;MRR0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;118_000&lt;/span&gt;         &lt;span class="c1"&gt;# January MRR
&lt;/span&gt;&lt;span class="n"&gt;GROWTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.015&lt;/span&gt;         &lt;span class="c1"&gt;# MRR growth per month
&lt;/span&gt;&lt;span class="n"&gt;VARIABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;        &lt;span class="c1"&gt;# sending infrastructure, payment fees, support: share of MRR
&lt;/span&gt;&lt;span class="n"&gt;FIXED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;74_000&lt;/span&gt;         &lt;span class="c1"&gt;# today's 9 people, tools and rent, per month
&lt;/span&gt;&lt;span class="n"&gt;LOADED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;17_500&lt;/span&gt;        &lt;span class="c1"&gt;# one engineer, fully loaded, per month
&lt;/span&gt;&lt;span class="n"&gt;RECRUIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25_000&lt;/span&gt;       &lt;span class="c1"&gt;# one-time hiring cost in the start month
&lt;/span&gt;&lt;span class="n"&gt;FLOOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;              &lt;span class="c1"&gt;# months of total costs to keep in the bank
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tightest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;START_CASH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MRR0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;GROWTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;heads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;costs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;VARIABLE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;FIXED&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;heads&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;LOADED&lt;/span&gt;
        &lt;span class="n"&gt;cash&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;costs&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;RECRUIT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;headroom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cash&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;FLOOR&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;costs&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tightest&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;headroom&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;tightest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tightest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;headroom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tightest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;

&lt;span class="n"&gt;plans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no hires&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1 hire: Feb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2 hires: Feb, Jun&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2 hires: Feb, Oct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3 hires: Feb, Jun, Sep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3 hires: all in Feb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;plans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BELOW FLOOR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Dec cash $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  tightest headroom $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in month &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;no hires                Dec cash $  763,090  tightest headroom $  147,600 in month  1  ok
1 hire: Feb             Dec cash $  545,590  tightest headroom $   73,354 in month  2  ok
2 hires: Feb, Jun       Dec cash $  398,090  tightest headroom $  -13,274 in month 11  BELOW FLOOR
2 hires: Feb, Oct       Dec cash $  468,090  tightest headroom $   56,726 in month 11  ok
3 hires: Feb, Jun, Sep  Dec cash $  303,090  tightest headroom $ -159,808 in month 12  BELOW FLOOR
3 hires: all in Feb     Dec cash $  110,590  tightest headroom $ -352,308 in month 12  BELOW FLOOR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cash model tells you whether you can afford the engineer. This short is about the other half of the decision, the return each hire should produce, which is the question I'd ask right after.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Mi3vZppVFLQ" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the model say about 2027 hires?
&lt;/h2&gt;

&lt;p&gt;One engineer in February is comfortable. The tightest month still leaves about $73,400 above the floor. A second engineer in June pushes cash about $13,300 under the floor by November. Move that second start to October and the tightest month keeps about $56,700 of headroom. Three hires in 2027 break the floor in both timings tested.&lt;/p&gt;

&lt;p&gt;The difference between June and October for the second hire is $70,000 of cash by December: four months of loaded cost at $17,500. Same person, same salary, different start date.&lt;/p&gt;

&lt;p&gt;Hiring all three in February is the clearest warning. By December the business is losing about $15,300 a month before tax, and cash is about $352,300 short of the floor. The cash runway hasn't run out yet, but the trend has turned.&lt;/p&gt;

&lt;p&gt;None of this means "don't hire". It means sequence the hires so each one starts when the business can carry it, and decide in advance what has to be true before the next offer goes out, such as MRR above a set number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you change before trusting the output?
&lt;/h2&gt;

&lt;p&gt;Replace every input with your own numbers, then stress them. Run growth at half your plan, add a month where a large customer churns, and check what happens if the recruiting fee lands a month before anyone starts. If a hiring plan only clears the floor with optimistic growth, it doesn't clear the floor.&lt;/p&gt;

&lt;p&gt;Two things the simple version leaves out on purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue from the new hire. The model assumes none, which is conservative. If an engineer is meant to unlock a feature customers will pay for, add that revenue as its own scenario, starting months after the hire.&lt;/li&gt;
&lt;li&gt;Raises and taxes. Add both once you have real numbers for them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you later raise funds, this same monthly view becomes the base for your revenue forecast, financial projections, scenario planning and board reporting. The &lt;a href="https://bennettfinancials.com/financial-plans-for-saas-companies-a-fractional-cfo-framework-for-predictable-growth-and-cash-control" rel="noopener noreferrer"&gt;SaaS financial planning framework&lt;/a&gt; I use builds on exactly this kind of month-by-month cash model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do on Monday?
&lt;/h2&gt;

&lt;p&gt;Paste the script, replace the inputs with your real numbers, and add every hire you're considering for 2027 as its own scenario. Pick a cash floor you'd defend to a co-founder. Then write down, for each hire, the MRR level or date that has to be reached before the offer goes out, and put it in the plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much cash should a bootstrapped SaaS keep before hiring?
&lt;/h3&gt;

&lt;p&gt;There's no single answer. Three months of total costs is the floor I'd start with for a profitable bootstrapped company. Pick a number you'd defend and stick to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the fully loaded cost of an engineer?
&lt;/h3&gt;

&lt;p&gt;Base salary plus employer payroll taxes, benefits, equipment and software. Use the monthly figure in the model and add recruiting as a one-time cost in the start month.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should the model include revenue from the new hire?
&lt;/h3&gt;

&lt;p&gt;Not in the base case. Add it as a separate scenario with a realistic delay, so the decision doesn't depend on revenue that hasn't happened yet.&lt;/p&gt;

</description>
      <category>management</category>
      <category>python</category>
      <category>startup</category>
    </item>
    <item>
      <title>A Financial Dashboard That Isn't Noise: Five Numbers a Small SaaS Can Pull With Python</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Mon, 02 Nov 2026 13:34:05 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/a-financial-dashboard-that-isnt-noise-five-numbers-a-small-saas-can-pull-with-python-10a9</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/a-financial-dashboard-that-isnt-noise-five-numbers-a-small-saas-can-pull-with-python-10a9</guid>
      <description>&lt;p&gt;&lt;em&gt;Arron Bennett, Founder of Bennett Financials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most small SaaS teams I meet have a financial dashboard. Few of them read it. It has a chart for every metric the tool could compute, it moves every day, and nobody can say what it's telling them to do.&lt;/p&gt;

&lt;p&gt;Most of a dashboard is expensive noise. It makes you feel busy while the business does whatever it was already going to do. A short list of numbers actually decides whether a company scales or just gets bigger and poorer.&lt;/p&gt;

&lt;p&gt;This post builds that short list in plain Python, using a hypothetical bootstrapped online-booking SaaS for escape rooms at $46,000 of MRR, five people, and a dashboard with 23 charts nobody reads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Five company-level numbers cover most of what a small SaaS needs to steer by: growth, gross margin, sales and marketing as a share of revenue, pre-tax operating margin and months of cash.&lt;/li&gt;
&lt;li&gt;Compare three-month windows, not single months. Quarterly trends are signal; monthly moves are mostly noise.&lt;/li&gt;
&lt;li&gt;Set a target for each number before you look, and flag only what misses.&lt;/li&gt;
&lt;li&gt;In this example, five numbers show what 23 charts hid: marketing spend rose sharply and growth barely moved.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What should a financial dashboard for a small SaaS show?
&lt;/h2&gt;

&lt;p&gt;A handful of financial metrics that each lead to a decision. For a small SaaS, I'd start with five: monthly revenue growth, gross margin, sales and marketing as a share of revenue, pre-tax operating margin, and months of costs in the bank. If a number can't change what you do next month, leave it off.&lt;/p&gt;

&lt;p&gt;Everything else, such as traffic, sign-ups, feature usage and support tickets, belongs to the team that runs it. It can live in their tools. The financial dashboard is for the few key performance indicators that tell you whether the business is getting healthier.&lt;/p&gt;

&lt;p&gt;Notice what's not on the list: anything per customer. You don't need customer-level economics to see whether the company is on track. Company totals from your books are enough, and they're numbers your accountant already produces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are 23 charts worse than five numbers?
&lt;/h2&gt;

&lt;p&gt;Because more charts means more noise, and noise hides signal. Daily and monthly numbers jump around for reasons that don't matter: a big annual renewal, a holiday month, a vendor bill paid early. With 23 charts, something is always up or down, so nothing stands out, and the one real trend gets lost.&lt;/p&gt;

&lt;p&gt;The fix is two habits. First, prune to the numbers that drive decisions. Second, compare windows, not points. Three months against the three months before is long enough to smooth out monthly noise and short enough to catch a trend while you can still act on it. That's signal vs noise in one rule.&lt;/p&gt;

&lt;p&gt;In this short I rank the numbers I'd actually steer a business by, starting with gross margin, the one most owners get wrong by a mile.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/w-qlmVj50wg" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the Python look like?
&lt;/h2&gt;

&lt;p&gt;One CSV export with a row per month and six columns: month, revenue, cost of revenue, sales and marketing, other operating costs and ending cash. Most accounting tools can export a monthly P&amp;amp;L, and the bank balance comes from your statements. The script computes the five numbers for two three-month windows and flags any that miss your targets.&lt;/p&gt;

&lt;p&gt;Mapping your accounts into those four cost buckets is the only fiddly part. Cost of revenue is hosting, payment processing and support. Sales and marketing includes salaries and tools, not just ads. Everything else goes in other operating costs. If your chart of accounts makes that hard, that's worth fixing on its own, and it's one of the more common &lt;a href="https://bennettfinancials.com/saas-accounting-problems" rel="noopener noreferrer"&gt;SaaS accounting problems&lt;/a&gt; I see.&lt;/p&gt;

&lt;p&gt;The script uses only the standard library. The sample data is inline so you can paste it and run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;

&lt;span class="n"&gt;CSV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;month,revenue,cost_of_revenue,sales_marketing,other_opex,ending_cash
2026-05,40800,8900,4900,23800,234200
2026-06,41900,9100,5300,23800,237900
2026-07,42700,9500,6400,24100,240600
2026-08,43900,9800,7100,24100,243500
2026-09,45100,10200,7600,24400,246400
2026-10,46000,10600,8200,24700,248900
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;TARGETS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;# your own lines: (target, "min" or "max")
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MRR growth per month&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gross margin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sales &amp;amp; marketing % of revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pre-tax operating margin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;months of costs in the bank&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;  &lt;span class="c1"&gt;# or: csv.DictReader(open("export.csv"))
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;month&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;five_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost_of_revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sales_marketing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;costs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cor&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;other_opex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;growth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MRR growth per month&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;growth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gross margin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;cor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sales &amp;amp; marketing % of revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pre-tax operating margin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;costs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;months of costs in the bank&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ending_cash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;costs&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;five_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nf"&gt;five_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;number&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;May-Jul&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Aug-Oct&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  flag&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TARGETS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;months&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nf"&gt;else &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;sign&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CHECK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;number                            May-Jul  Aug-Oct   target  flag
MRR growth per month                 2.3%     2.4%   &amp;gt;=2.0%  ok
gross margin                        78.1%    77.3%  &amp;gt;=75.0%  ok
sales &amp;amp; marketing % of revenue      13.2%    17.0%  &amp;lt;=15.0%  CHECK
pre-tax operating margin             7.7%     6.1%  &amp;gt;=10.0%  CHECK
months of costs in the bank           6.2      5.9    &amp;gt;=6.0  CHECK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Growth is the compound monthly growth across each window. Months of costs in the bank is ending cash divided by average monthly total costs in that window. Everything is pre-tax.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do the five numbers say about this business?
&lt;/h2&gt;

&lt;p&gt;Marketing spend jumped and growth didn't follow. Sales and marketing went from 13.2% of revenue to 17.0%, past a 15% line. Monthly growth barely moved, from 2.3% to 2.4%. Pre-tax operating margin fell from 7.7% to 6.1%, and months of costs in the bank slipped under six.&lt;/p&gt;

&lt;p&gt;In dollars, the team spent $6,300 more on sales and marketing in August to October than in May to July, about $2,100 a month. That spend bought almost no extra growth. Gross margin slipped a little, from 78.1% to 77.3%, which is worth watching but still clears the target.&lt;/p&gt;

&lt;p&gt;Each of those numbers was somewhere in the 23 charts. Nobody saw them together, because they were spread across tabs and each one moved a bit every day. Five lines with a target and a flag make the story obvious in ten seconds.&lt;/p&gt;

&lt;p&gt;The decision follows from it. Find out which channel took the extra $2,100 a month, give it one more month with a clear test, and cut it if growth doesn't move. That's the job of a dashboard: to start the right conversation, quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you stop the dashboard growing back to 23 charts?
&lt;/h2&gt;

&lt;p&gt;Make every number earn its place. Before adding a metric, write down the decision it would change and the target that would trigger it. If you can't, don't add it. Review the list once a quarter and drop anything nobody has acted on. Five numbers read monthly beat fifty glanced at daily.&lt;/p&gt;

&lt;p&gt;A few practical rules I'd add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the targets in the code, next to the numbers, so they're reviewed together.&lt;/li&gt;
&lt;li&gt;Run it once a month after the books close, not daily. Daily runs bring the noise back.&lt;/li&gt;
&lt;li&gt;Add a sixth number only when the business changes, for example net cash flow once you start borrowing, or close rate once you hire salespeople.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a longer list to choose from, here are &lt;a href="https://bennettfinancials.com/10-financial-kpis-every-founder-must-monitor-according-to-cfos" rel="noopener noreferrer"&gt;ten financial KPIs&lt;/a&gt; worth knowing. Pick the ones that match decisions you actually make.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do on Monday?
&lt;/h2&gt;

&lt;p&gt;Export six months of P&amp;amp;L by month, add the ending bank balance for each, and map your accounts into the four cost buckets. Paste the script, swap in your CSV, and set your own targets. Then read the five lines with your co-founder or team and agree on one action for any number that misses.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the most important number on a SaaS financial dashboard?
&lt;/h3&gt;

&lt;p&gt;Gross margin. It decides how much of every revenue dollar is left to fund growth and overhead. If it's weak, every other number has less room to work.&lt;/p&gt;

&lt;h3&gt;
  
  
  How often should a small SaaS review its financial dashboard?
&lt;/h3&gt;

&lt;p&gt;Monthly, after the books close. Compare the last three months with the three before, so a single unusual month doesn't drive a decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should operating cash flow be on the dashboard?
&lt;/h3&gt;

&lt;p&gt;For most small SaaS companies, months of costs in the bank and pre-tax operating margin cover it. Add operating cash flow when timing gaps grow, such as annual prepayments or slow-paying customers.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>python</category>
      <category>saas</category>
      <category>startup</category>
    </item>
    <item>
      <title>SaaS Due Diligence: What Buyers Will Ask a Technical Founder For</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Fri, 30 Oct 2026 12:55:44 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/saas-due-diligence-what-buyers-will-ask-a-technical-founder-for-6f7</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/saas-due-diligence-what-buyers-will-ask-a-technical-founder-for-6f7</guid>
      <description>&lt;p&gt;&lt;em&gt;Arron Bennett, Founder of Bennett Financials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The letter of intent feels like the finish line. It isn't. It's the start of due diligence, the 45 to 90 days where a buyer checks everything you told them, and often finds a few things you didn't.&lt;/p&gt;

&lt;p&gt;Technical founders tend to expect the code review and dread the finance questions. In practice, the code is rarely what moves the price. Messy revenue data, missing paperwork and a couple of large customers do that.&lt;/p&gt;

&lt;p&gt;This post covers what buyers ask for, how to rebuild your MRR history in SQL before they do, and the findings that most often cut the price.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Due diligence checks revenue quality, the code and its ownership, contracts and people. Revenue is where most price cuts start.&lt;/li&gt;
&lt;li&gt;Have a month-by-month MRR rollforward ready: new, expansion, contraction and churn. It should tie to your billing system and your books.&lt;/li&gt;
&lt;li&gt;Every contractor and employee who wrote code should have signed an IP assignment. Missing ones get fixed before closing, at your cost.&lt;/li&gt;
&lt;li&gt;Customer concentration, deferred revenue and open-source licences are the usual surprises.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is SaaS due diligence?
&lt;/h2&gt;

&lt;p&gt;SaaS due diligence is the buyer's check of your business after you sign a letter of intent and before the deal closes. They verify revenue and retention, review the code and who owns it, read customer and vendor contracts, and look at security, infrastructure and people. Anything that doesn't match what you told them becomes a price cut, an escrow or a reason to walk away.&lt;/p&gt;

&lt;h2&gt;
  
  
  What financial data will buyers ask for?
&lt;/h2&gt;

&lt;p&gt;Expect requests for monthly P&amp;amp;L and balance sheets for two to three years, a customer list with MRR by customer, an MRR rollforward, churn and retention by cohort, deferred revenue, and your billing system export. The buyer will rebuild your numbers from raw billing data. If their numbers don't match yours, they'll trust theirs.&lt;/p&gt;

&lt;p&gt;That's why the most useful thing you can do before a sale is build the rollforward yourself. This query works on a table with one row per customer per month and their MRR that month (PostgreSQL):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- MRR rollforward: new, expansion, contraction and churn by month&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;LAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mrr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;LAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;prev_month&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customer_mrr&lt;/span&gt;            &lt;span class="c1"&gt;-- one row per customer per month with mrr &amp;gt;= 0&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;prev_month&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'1 month'&lt;/span&gt;
           &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                   &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;new_mrr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;prev_month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'1 month'&lt;/span&gt;
           &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                        &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;expansion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;prev_month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'1 month'&lt;/span&gt;
           &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                        &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;contraction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;prev_mrr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;mrr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;prev_month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'1 month'&lt;/span&gt;
           &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;prev_mrr&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                             &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;churn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mrr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                                        &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ending_mrr&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this to count churn, a cancelled customer needs a row with &lt;code&gt;mrr = 0&lt;/code&gt; in the month after they leave. A returning customer after a gap shows as new MRR, which is how most buyers treat reactivations. Check that each month's opening MRR plus new, expansion, contraction and churn equals the ending MRR. Then check the totals tie to revenue in your books. If they don't, find out why before the buyer does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What will buyers check in the code?
&lt;/h2&gt;

&lt;p&gt;Less about style, more about risk. Expect questions on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ownership.&lt;/strong&gt; Who wrote the code, and did each of them sign an IP assignment?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-source licences.&lt;/strong&gt; A scan for copyleft licences that could force you to release your own code if you distribute it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security.&lt;/strong&gt; Past incidents, pen test results, access controls and how secrets are stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure.&lt;/strong&gt; What it costs to run, what's documented, and whether it depends on one person's personal accounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bus factor.&lt;/strong&gt; Whether the product survives if one engineer, often you, leaves after the sale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run a licence scan and list every person who's committed code before you list the company. Both are cheap to do early and slow to do mid-deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What findings cut the price?
&lt;/h2&gt;

&lt;p&gt;Here's a hypothetical example. Every number is made up to show the mechanics.&lt;/p&gt;

&lt;p&gt;A compliance-automation SaaS has $1.43 million of ARR. A buyer signs an LOI at 4.2 times ARR, about $6.0 million. Diligence finds three issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deferred revenue.&lt;/strong&gt; Customers prepaid $310,000 for service still to be delivered. The buyer will have to deliver it without being paid again, so they treat it as a debt-like item and want it deducted from the price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP assignments.&lt;/strong&gt; Two former contract developers never signed one. The buyer makes signed assignments a condition of closing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer concentration.&lt;/strong&gt; One customer is 14% of ARR, with a contract that renews in four months.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After negotiation, the founders agree to a $5.6 million price and pay a lawyer to track down the two contractors. They also lose a few weeks. The code review, which they'd worried about most, found nothing that changed the price.&lt;/p&gt;

&lt;p&gt;I talk about the gap between a business that's an asset and one that's a job in this video. Buyers pay for the asset. Diligence is how they find out which one they're buying.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/p2ySifx7JWg" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you prepare before you sell?
&lt;/h2&gt;

&lt;p&gt;Start six to twelve months before you plan to sell. Build the MRR rollforward and reconcile it to your books. Collect signed IP assignments from everyone who has written code. Run a licence scan. Move infrastructure out of personal accounts. Write down how to deploy and run the product. Reduce your reliance on any one customer where you can. And know which items on your balance sheet a buyer will treat like debt. I've written a fuller guide on &lt;a href="https://bennettfinancials.com/debt-like-items-business-sale" rel="noopener noreferrer"&gt;debt-like items in a business sale&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do on Monday?
&lt;/h2&gt;

&lt;p&gt;Run the rollforward query on your billing data and check it against last year's revenue in your books. Then list every person who has committed to your main repository and confirm you have a signed IP assignment for each. Those two checks catch the problems that most often cost founders money in diligence.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How long does SaaS due diligence take?
&lt;/h3&gt;

&lt;p&gt;Often 45 to 90 days from the signed LOI, longer if the data room is incomplete or the buyer finds issues that need fixing.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a data room?
&lt;/h3&gt;

&lt;p&gt;A shared folder of documents buyers review during diligence: financials, contracts, cap table, IP assignments, security reports and policies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do buyers review the source code?
&lt;/h3&gt;

&lt;p&gt;Usually, either directly or through a third-party code scan for licences, security and quality. Smaller buyers sometimes rely on a scan and a call with the lead engineer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is deferred revenue treated as debt in a sale?
&lt;/h3&gt;

&lt;p&gt;Because the buyer must deliver service customers have already paid for, without receiving that cash. Some buyers deduct the full amount and some only the cost to deliver it, so it's a point to negotiate.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>software</category>
      <category>startup</category>
    </item>
    <item>
      <title>Venture Debt for SaaS Founders: How It Works and When It Turns Dangerous</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Thu, 29 Oct 2026 12:40:45 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/venture-debt-for-saas-founders-how-it-works-and-when-it-turns-dangerous-2olb</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/venture-debt-for-saas-founders-how-it-works-and-when-it-turns-dangerous-2olb</guid>
      <description>&lt;p&gt;&lt;em&gt;Arron Bennett, Founder of Bennett Financials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A few weeks after a Series A closes, the term sheets start arriving. Not from investors. From lenders. "Add $3 million of runway with minimal dilution." It sounds like free time.&lt;/p&gt;

&lt;p&gt;It isn't free, and it usually isn't as much time as it sounds. Venture debt can be a smart tool for a SaaS company with strong unit economics and a clear plan. It can also turn a slow quarter into a crisis, because a lender, unlike an investor, can ask for the money back.&lt;/p&gt;

&lt;p&gt;This post covers how venture debt works, how much runway it really adds, a short Python model you can run on your own numbers, and the terms that make it dangerous.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Venture debt is a term loan for venture-backed companies, usually with an interest-only period, then monthly principal payments, plus fees and warrants.&lt;/li&gt;
&lt;li&gt;It adds less runway than the headline suggests, because repayments start before most companies raise again.&lt;/li&gt;
&lt;li&gt;It's cheapest and safest when it funds growth with a known payback, not when it's the plan for survival.&lt;/li&gt;
&lt;li&gt;Read the covenants and the default clauses before the interest rate. They decide how dangerous the loan is.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is venture debt?
&lt;/h2&gt;

&lt;p&gt;Venture debt is a loan to a venture-backed startup, usually sized as a fraction of the last equity round. Typical terms include an interest-only period, then monthly principal repayments, a final payment fee and warrants that give the lender a small right to buy equity. It costs less dilution than equity, but it must be repaid on schedule whatever happens to growth.&lt;/p&gt;

&lt;p&gt;Lenders lend because your investors just backed you. The equity round is their comfort. That's also why the timing of your next round matters so much.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do venture debt terms work?
&lt;/h2&gt;

&lt;p&gt;Most venture debt offers have the same moving parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Draw period&lt;/strong&gt;: how long you have to take the money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interest-only period&lt;/strong&gt;: months where you pay only interest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amortization&lt;/strong&gt;: after interest-only ends, principal is repaid in equal monthly amounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final payment fee&lt;/strong&gt;: a percentage of the loan paid at the end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warrants&lt;/strong&gt;: a right for the lender to buy a small amount of equity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Covenants&lt;/strong&gt;: rules you must keep, such as a minimum cash balance or revenue targets, plus default clauses like a "material adverse change" clause.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interest rate gets the attention. The covenants and default clauses decide what happens when things go wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much runway does venture debt really add?
&lt;/h2&gt;

&lt;p&gt;Less than cash ÷ burn suggests. Interest starts on day one, and principal payments usually begin twelve months or so later, often right when you'd be raising the next round. This model shows the effect. It's simplified: fixed rate, the loan drawn on day one, flat net burn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;runway_months&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;net_burn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interest_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;amort_months&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;final_fee&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Months until cash runs out, with an optional term loan drawn today.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;cash&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;loan&lt;/span&gt;
    &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;interest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
        &lt;span class="n"&gt;principal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;interest_only&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loan&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;amort_months&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;net_burn&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;interest&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;principal&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;loan&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;interest_only&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;amort_months&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;loan&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;final_fee&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cash&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;   &lt;span class="c1"&gt;# fraction of the last month
&lt;/span&gt;        &lt;span class="n"&gt;cash&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
        &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;principal&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No debt:   &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;runway_months&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8_800_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; months&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$3M loan:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;runway_months&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8_800_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; months&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No debt:   22.0 months
$3M loan:  24.8 months
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap in your own cash, burn and the terms on your offer. Then add a version where burn rises, because it usually does once the money lands.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does that look like for a real SaaS company?
&lt;/h2&gt;

&lt;p&gt;Here's a hypothetical example. Every number is made up to show the mechanics.&lt;/p&gt;

&lt;p&gt;A Series A observability SaaS has $4.1 million in ARR, $8.8 million in the bank and $400,000 a month of net burn. That's 22 months of runway. A lender offers $3 million: 11% interest, 12 months interest-only, 30 months of principal repayments, a 4% final payment fee and warrants.&lt;/p&gt;

&lt;p&gt;Naive math says $3 million is 7.5 more months. The model says 2.8.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interest over the life of the loan comes to about $756,000.&lt;/li&gt;
&lt;li&gt;The final payment fee is $120,000.&lt;/li&gt;
&lt;li&gt;At month 22, when the company would have run out of cash without the loan, it still owes $2 million.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the founders would pay about $876,000 in interest and fees, plus warrants, for under three extra months, and enter their Series B process with $100,000 a month of principal going out the door.&lt;/p&gt;

&lt;p&gt;That doesn't make the loan a bad idea. It makes it a bad idea for buying time. It can be a good idea for buying growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  When is venture debt a good idea?
&lt;/h2&gt;

&lt;p&gt;Venture debt works best when it funds something with a known, fast payback. If every dollar of sales and marketing comes back within twelve months and customers stay for years, borrowing to spend more of those dollars sooner can be cheaper than raising equity for it.&lt;/p&gt;

&lt;p&gt;This video isn't about venture debt. It's about the moment when holding cash back costs more than spending it, which is the same test I'd apply to borrowed money.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/FgDmIUbt1gI" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It's also reasonable as insurance: drawn only if a round takes longer than planned, with terms that allow that. Some lenders let you draw later in the draw period, which keeps interest down until you need the money.&lt;/p&gt;

&lt;h2&gt;
  
  
  When does venture debt turn dangerous?
&lt;/h2&gt;

&lt;p&gt;It turns dangerous when growth slows and the loan's rules start to bite. Watch for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimum cash covenants.&lt;/strong&gt; A covenant that requires you to keep, say, a set amount of cash in the bank shrinks your usable runway by that amount.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revenue or performance covenants.&lt;/strong&gt; Miss a target and you're in default, even if you're paying on time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Material adverse change clauses.&lt;/strong&gt; Broad wording can let the lender call the loan if they decide your prospects worsened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repayments that start before the next round.&lt;/strong&gt; If principal starts at month 12 and your raise slips to month 20, you're repaying debt with the cash meant to get you there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Banking requirements.&lt;/strong&gt; Many lenders require your operating accounts at their bank. After the 2023 bank failures, founders understand why that concentration matters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A default doesn't mean the lender takes the company the next morning. It does mean the conversation is on their terms, at the worst possible time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do on Monday?
&lt;/h2&gt;

&lt;p&gt;Run the model with your real cash, burn and offer terms. Then run it again with burn 20% higher and your next round six months late. If the loan only works in the first version, don't sign it. If you go ahead, send the covenants, default clauses and warrant terms to your lawyer before you negotiate the rate. If you're also weighing other non-dilutive options, I've written about &lt;a href="https://bennettfinancials.com/line-of-credit-borrowing-base" rel="noopener noreferrer"&gt;how a line of credit and its borrowing base work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is venture debt cheaper than equity?
&lt;/h3&gt;

&lt;p&gt;Usually less dilutive, not always cheaper. Interest, fees and warrants are real costs, and repayments compete with growth for cash. It's cheapest when it funds growth with a fast, proven payback.&lt;/p&gt;

&lt;h3&gt;
  
  
  When should a startup take venture debt?
&lt;/h3&gt;

&lt;p&gt;Typically soon after an equity round, while the company has plenty of cash and the lender is comfortable. Taking it when you're already low on cash usually means worse terms.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are warrants in venture debt?
&lt;/h3&gt;

&lt;p&gt;A right for the lender to buy a small amount of equity, usually at the last round's price. It's a small dilution, but it's part of the cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can venture debt be repaid early?
&lt;/h3&gt;

&lt;p&gt;Often, yes, but many loans charge a prepayment fee. Check the terms before you assume you can refinance or repay after your next round.&lt;/p&gt;

</description>
      <category>funding</category>
      <category>saas</category>
      <category>startup</category>
    </item>
    <item>
      <title>Your Stripe Payouts Are Not Your Revenue: How to Reconcile Stripe to Your Books</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Tue, 27 Oct 2026 13:35:45 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/your-stripe-payouts-are-not-your-revenue-how-to-reconcile-stripe-to-your-books-dp</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/your-stripe-payouts-are-not-your-revenue-how-to-reconcile-stripe-to-your-books-dp</guid>
      <description>&lt;p&gt;Arron Bennett, Founder of Bennett Financials&lt;/p&gt;

&lt;p&gt;Most bootstrapped SaaS books I open have the same quiet error. Every Stripe payout landed in the bank, and someone booked each one as revenue. The bank matches. The books look clean. The revenue number is still wrong, and so is every margin built on top of it.&lt;/p&gt;

&lt;p&gt;A Stripe payout isn't a sale. It's a bundle of sales, minus fees, minus refunds, minus disputes, sent a few days after the money came in. This post covers what a payout actually contains, how to split one apart with a short script, and the bookkeeping setup that keeps the two in step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Stripe payout is a net number. Gross charges, Stripe fees, refunds and disputes are all inside it.&lt;/li&gt;
&lt;li&gt;Booking payouts as revenue understates revenue, hides fees, and buries refunds.&lt;/li&gt;
&lt;li&gt;Book gross charges as revenue and fees as an expense, then run both through a Stripe clearing account.&lt;/li&gt;
&lt;li&gt;The balance transactions API (or the Payout reconciliation report) tells you exactly what each payout contains.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What does Stripe reconciliation actually mean?
&lt;/h2&gt;

&lt;p&gt;Stripe reconciliation means proving that every payout in your bank equals the charges, fees, refunds and disputes Stripe says made it up, and that each piece is booked in the right account. Revenue goes to revenue, fees to expenses, refunds against revenue. When the clearing account nets to zero, you're reconciled.&lt;/p&gt;

&lt;p&gt;Your bank only ever sees the net. Your books need the gross.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are Stripe payouts not the same as revenue?
&lt;/h2&gt;

&lt;p&gt;Stripe takes its fees out before it pays you. Refunds and lost disputes come out of the same balance, and payouts arrive on a schedule, so charges from the last days of a month land in next month's bank account. Book the deposit as revenue and every one of those differences disappears into a single wrong number.&lt;/p&gt;

&lt;p&gt;Here's a hypothetical example. A two-founder developer-docs hosting SaaS at $29,000 of MRR, all on Stripe. Every number below is made up to show the mechanics. Swap in yours.&lt;/p&gt;

&lt;p&gt;In one month the business charges customers $31,200. It refunds $640, loses one $480 dispute plus a $15 dispute fee (an assumed figure, so check your own), and pays $1,150 in processing fees.&lt;/p&gt;

&lt;p&gt;So the payouts total $28,915, which is $31,200 − $640 − $480 − $15 − $1,150.&lt;/p&gt;

&lt;p&gt;If the bookkeeper records $28,915 as revenue, revenue is understated by $2,285. That's $1,165 of fees and dispute costs that never show up as an expense, and $1,120 of refunds and lost disputes that never show up as anything. The fees alone are 3.7% of gross, and they have no line on the P&amp;amp;L.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you see what's inside a Stripe payout?
&lt;/h2&gt;

&lt;p&gt;Every movement in your Stripe balance is a balance transaction with a gross &lt;code&gt;amount&lt;/code&gt;, a &lt;code&gt;fee&lt;/code&gt; and a &lt;code&gt;net&lt;/code&gt;, all in the smallest currency unit, so cents for USD. For automatic payouts, you can list every balance transaction that was paid out in one payout and group them by &lt;code&gt;reporting_category&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's a minimal Python version. Use a restricted, read-only key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;

&lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;STRIPE_READ_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# restricted, read-only
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;explain_payout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payout_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;payout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Payout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payout_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;totals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gross&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fee&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;txns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BalanceTransaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payout_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;txns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auto_paging_iter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;  &lt;span class="c1"&gt;# the payout itself, not what's inside it
&lt;/span&gt;        &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;totals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reporting_category&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gross&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fee&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fee&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;
    &lt;span class="n"&gt;net_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;net&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;totals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; gross &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gross&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  fee &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fee&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  net &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;net&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payout &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | sum of nets &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;net_sum&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | match: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;net_sum&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;payout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;explain_payout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;po_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last line is the check that matters. If the sum of nets equals the payout amount, you know exactly what's in that deposit. If it doesn't, the gap is something you need to find, such as an adjustment, a reserve or a manual payout. The &lt;code&gt;payout&lt;/code&gt; filter only works for automatic payouts. If you trigger payouts by hand, use the Payout reconciliation report in the Stripe Dashboard instead.&lt;/p&gt;

&lt;p&gt;You don't need to run this on every payout forever. Run it on one month, prove the method, then let your bookkeeping tool's Stripe integration or a monthly report do the routine work.&lt;/p&gt;

&lt;h2&gt;
  
  
  How should Stripe be set up in the books?
&lt;/h2&gt;

&lt;p&gt;Treat Stripe like a bank account. Create a "Stripe clearing" account, record gross charges into it as revenue, take fees out of it as an expense, take refunds out of it against revenue, and record each payout as a transfer from clearing to the bank. At month end, the clearing balance should equal the Stripe balance.&lt;/p&gt;

&lt;p&gt;For the docs-hosting example, the month books like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue: $31,200 of gross charges.&lt;/li&gt;
&lt;li&gt;Refunds and lost disputes: $1,120, against revenue.&lt;/li&gt;
&lt;li&gt;Payment processing fees: $1,165, as an operating expense.&lt;/li&gt;
&lt;li&gt;Transfers to the bank: $28,915, which revenue never touches.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The clearing account goes up by $31,200 and down by $1,120, $1,165 and $28,915, so it lands on zero. If payouts lag the month end, it won't be zero. It'll equal whatever Stripe still holds for you, and that's correct. A balance that drifts every month means something is being missed.&lt;/p&gt;

&lt;p&gt;One more thing worth watching. If Stripe ever holds a reserve on your account, that money is yours but you can't spend it. It belongs in clearing, not in your cash forecast. I've written more on &lt;a href="https://bennettfinancials.com/payment-processor-reserves-available-cash" rel="noopener noreferrer"&gt;processor reserves and available cash&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This short isn't about Stripe. It's about the same gap from the other side: a profitable P&amp;amp;L and a bank account that can't cover payroll. That gap is the reason I want the books and the bank telling the same story.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Gzh4-UBay7A" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when the books are right?
&lt;/h2&gt;

&lt;p&gt;Three things get honest at once. Revenue shows what customers really paid. Processing fees show up as a cost you can manage. And refunds become visible, so a product or onboarding problem stops hiding inside a smaller deposit.&lt;/p&gt;

&lt;p&gt;That matters the first time you look at gross margin, price a plan, or talk to anyone who will read your numbers closely, such as a lender, an investor or a buyer. Diligence on a SaaS business reconciles Stripe to the bank and the books. Better that you find the gaps first.&lt;/p&gt;

&lt;p&gt;It also makes refunds a number you can act on. If you find refunds climbing, look at &lt;a href="https://bennettfinancials.com/saas-failed-payment-recovery-retention" rel="noopener noreferrer"&gt;failed payments and recovery&lt;/a&gt; in the same review. Both are revenue leaving quietly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you do on Monday?
&lt;/h2&gt;

&lt;p&gt;Pick last month. Pull every payout, run the script or the Payout reconciliation report, and total gross charges, fees, refunds and disputes. Compare gross charges with the revenue in your books. If the books show the payout total instead, set up a Stripe clearing account and rebook the month the right way. Then make it the rule for every month after.&lt;/p&gt;

&lt;p&gt;It's a two-hour job. It fixes your revenue line for good.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should I record Stripe fees as an expense or net them against revenue?
&lt;/h3&gt;

&lt;p&gt;Record them as an expense. Revenue should be what customers paid. Fees are a cost of collecting it, and you can only manage a cost you can see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why doesn't my Stripe payout match my sales for the month?
&lt;/h3&gt;

&lt;p&gt;Because payouts are net of fees, refunds and disputes, and they arrive on a schedule. Sales from the last few days of a month often land in next month's bank deposits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does a Stripe payout include refunds and disputes?
&lt;/h3&gt;

&lt;p&gt;Yes. Refunds and lost disputes reduce your Stripe balance, so they come out of the next payout. A payout can even come out negative if refunds outrun charges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need software to reconcile Stripe?
&lt;/h3&gt;

&lt;p&gt;Not to start. The Payout reconciliation report or a short script covers one month. After that, most accounting tools' Stripe integrations can post gross, fees and refunds for you, once the clearing account is set up.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>saas</category>
      <category>startup</category>
    </item>
    <item>
      <title>Finding a CFO for Tech Startups: A Practical Guide for Developers &amp; Technical Founders (2025)</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Fri, 14 Nov 2025 13:15:14 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/finding-a-cfo-for-tech-startups-a-practical-guide-for-developers-technical-founders-2025-k0g</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/finding-a-cfo-for-tech-startups-a-practical-guide-for-developers-technical-founders-2025-k0g</guid>
      <description>&lt;p&gt;Technical founders tend to delay hiring a CFO for the same reason they delay hiring DevOps or QA: it feels faster to handle things themselves. That works in the earliest stages, but once you pass around $1M to $2M in revenue, finance becomes a scaling problem just like engineering. The moment you’re thinking about runway, hiring, pricing changes, fundraising, or subscription complexity, “DIY finance” stops being safe.&lt;/p&gt;

&lt;p&gt;A CFO brings the same discipline to your financial system that an engineering lead brings to your codebase. They turn scattered spreadsheets into real models, translate billing data into predictable metrics, and redesign your financial processes so you can grow without breaking something critical.&lt;/p&gt;

&lt;p&gt;Most developers realize they need a CFO when they can’t see more than a few months of cash runway. If your best forecast is a spreadsheet that collapses when one input changes, you’re working without version control. A CFO builds a rolling 13-week cash model that updates consistently and helps you understand exactly when you need to raise, slow down, or accelerate. They also take on the heavy lifting of investor prep, data rooms, due-diligence requests, and the financial side of fundraising—things that become extremely time-consuming for technical founders.&lt;/p&gt;

&lt;p&gt;Another sign you’re ready is billing complexity. Usage-based pricing, hybrid plans, and multi-year contracts all introduce revenue recognition rules that matter for SaaS. If your revenue logic feels more like code than accounting, you need someone who understands ASC 606 and how your billing system connects to your metrics. Getting this wrong can wreck your ARR, churn reporting, and investor conversations.&lt;/p&gt;

&lt;p&gt;For most early-stage startups, a fractional CFO makes more sense than a full-time hire. Fractional CFOs give you the forecasting, dashboards, investor support, and tax strategy of a true finance leader without the $200K–$350K salary or equity dilution. You get a senior strategist without having to build a full finance team. Once you’re past Series A and growing headcount, a full-time CFO becomes more important—but not before.&lt;/p&gt;

&lt;p&gt;The real test when evaluating a CFO is whether they can think through your business like a system. Give them a small anonymized example of your numbers, and see how they explain what’s happening. A strong candidate will reason like an engineer: identify constraints, question assumptions, and walk through how one change affects the whole architecture. Avoid candidates who only talk in generalities or who rely exclusively on spreadsheets without understanding automation or integrations.&lt;/p&gt;

&lt;p&gt;The first 90 days with a good CFO should feel like moving from ad-hoc scripts to stable infrastructure. Your books should close within ten days. Your cash forecast should finally be something you trust. Your metrics should be consistent across accounting, billing, and product. And you should have a clear, data-driven view of your path to the next milestone—whether that’s profitability, $10M ARR, or your next raise.&lt;/p&gt;

&lt;p&gt;If you're a developer or technical founder, think of hiring a CFO the same way you think about hiring your first senior engineer. It’s not about giving up control—it’s about building a system that scales faster than you can. When the financial side of the business becomes too complex to “quickly patch,” that’s when a CFO becomes one of the highest-leverage hires you can make.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Top 15 Fractional CFO Services (For Founders Who Think in Systems)</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Tue, 11 Nov 2025 14:16:57 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/the-top-15-fractional-cfo-services-for-founders-who-think-in-systems-4eg4</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/the-top-15-fractional-cfo-services-for-founders-who-think-in-systems-4eg4</guid>
      <description>&lt;ol&gt;
&lt;li&gt;Bennett Financials — The Full-Stack Finance Partner&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bennett Financials isn’t an accounting firm pretending to be strategic.&lt;br&gt;
We’re system thinkers who treat your finance stack like your tech stack.&lt;/p&gt;

&lt;p&gt;Here’s how:&lt;/p&gt;

&lt;p&gt;Architecture First: We map your entire financial infrastructure — cash flow, pricing logic, team costs, capital usage — just like a dev audits dependencies and services.&lt;/p&gt;

&lt;p&gt;Real-Time Dashboards: Live metrics (profitability, margin, burn, velocity) built to show actual performance drift, not static reports.&lt;/p&gt;

&lt;p&gt;Tax Strategy as Engineering Optimization: We find the equivalent of code efficiency inside your taxes — reducing cash taxes by 40–60% so your growth capital isn’t wasted on the IRS.&lt;/p&gt;

&lt;p&gt;API Mindset: We integrate finance data across your systems (QuickBooks, Stripe, Gusto, HubSpot, etc.) so you’re not trapped in spreadsheet hell.&lt;/p&gt;

&lt;p&gt;We help founders scale from $1M to $10M+ without breaking the backend — financially or operationally.&lt;/p&gt;

&lt;p&gt;👉 Book a call here&lt;br&gt;
 to see how we’d build your financial system for scale.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pilot&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Automation-heavy bookkeeping with CFO support. Great for early-stage SaaS founders who value speed and clean dashboards.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kruze Consulting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fundraising and VC prep pros — perfect for seed-to-Series-B startups.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CFOshare&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Excellent for scaling ops and ERP integration at mid-market level.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;NOW CFO&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Focuses on rolling 13-week cash flow forecasts — good for dev shops managing project-based revenue.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Preferred CFO&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Senior operators with corporate finance backgrounds. Hands-on strategy and restructuring.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Acuity&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Built for SaaS founders managing deferred revenue and ARR growth metrics.&lt;/p&gt;

&lt;p&gt;…and the rest of the top 15 follow suit — all credible firms with solid offerings.&lt;br&gt;
But none blend strategic finance, tax efficiency, and data intelligence quite like Bennett Financials.&lt;/p&gt;

&lt;p&gt;Why Devs and Founders Love Working With Us&lt;/p&gt;

&lt;p&gt;Because we think like you do.&lt;br&gt;
We debug numbers the same way you debug code.&lt;/p&gt;

&lt;p&gt;Our clients don’t want more reports — they want visibility.&lt;br&gt;
They want a system that tells them, “Here’s what’s working. Here’s what’s leaking. Here’s what to fix.”&lt;/p&gt;

&lt;p&gt;We bring clarity to chaos using tools, automation, and financial frameworks that scale.&lt;br&gt;
You get proactive strategy, not reactive accounting.&lt;/p&gt;

&lt;p&gt;The Developer’s ROI Equation&lt;/p&gt;

&lt;p&gt;Hiring a full-time CFO? Expect $200K+ in salary before equity and benefits.&lt;/p&gt;

&lt;p&gt;A fractional CFO through Bennett Financials delivers the same strategy at $60K–$120K annually, fully scalable based on stage and scope.&lt;/p&gt;

&lt;p&gt;The ROI?&lt;br&gt;
Every technical founder we work with learns exactly how financial decisions drive runway, hiring, pricing, and margin stability — so growth stops feeling like a guessing game.&lt;/p&gt;

&lt;p&gt;Final Commit: Build Financial Systems That Scale&lt;/p&gt;

&lt;p&gt;Your product has architecture.&lt;br&gt;
Your infrastructure has architecture.&lt;br&gt;
Your finances need one too.&lt;/p&gt;

&lt;p&gt;That’s where we come in.&lt;br&gt;
We help founders and dev teams build financial systems that don’t crash under scale.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://bennettfinancials.com/top-15-fractional-cfo-services-for-custom-financial-s&lt;br&gt;%0A![%20](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ee26bzg2s8r8h4qexlsq.png)olutions/" rel="noopener noreferrer"&gt;Read the full breakdown here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://bennettfinancials.com/contact-us/" rel="noopener noreferrer"&gt;Book a consultation&lt;/a&gt;&lt;br&gt;
 if you’re ready to scale smarter.&lt;/p&gt;

</description>
      <category>saas</category>
    </item>
    <item>
      <title>Why SaaS Companies Grow Faster with a Fractional CFO</title>
      <dc:creator>Micheal Slyderink</dc:creator>
      <pubDate>Mon, 10 Nov 2025 13:43:52 +0000</pubDate>
      <link>https://dev.to/micheal_slyderink_810b3f6/why-saas-companies-grow-faster-with-a-fractional-cfo-3cle</link>
      <guid>https://dev.to/micheal_slyderink_810b3f6/why-saas-companies-grow-faster-with-a-fractional-cfo-3cle</guid>
      <description>&lt;p&gt;SaaS growth is exciting until your numbers stop making sense. You’ve got rising MRR, new hires, maybe even investor interest—but somehow, cash flow still feels unpredictable. That disconnect usually isn’t about performance; it’s about forecasting.&lt;/p&gt;

&lt;p&gt;A fractional CFO helps SaaS founders replace reactive financial management with proactive control. Instead of guessing at runway, you see it clearly. Instead of scaling based on gut feeling, you model your growth in advance.&lt;/p&gt;

&lt;p&gt;For most SaaS companies between $1M and $10M ARR, hiring a full-time CFO doesn’t make financial sense yet. A fractional CFO gives you executive-level clarity at a fraction of the cost—building forecasts, cash models, and investor-ready reports that make scaling sustainable.&lt;/p&gt;

&lt;p&gt;They turn data into strategy, showing how churn, pricing, CAC, and headcount all flow through to real profitability. You stop running on hope and start running on visibility.&lt;/p&gt;

&lt;p&gt;At Bennett Financials, we work with scaling SaaS companies to connect financial strategy with product growth. Our team builds forecasting and budgeting systems that give founders what they need most—confidence in every decision.&lt;/p&gt;

&lt;p&gt;Learn more at Bennett Financials: &lt;a href="https://bennettfinancials.com/fractional-cfo-services-for-budgeting-and-forecasting-excellence/" rel="noopener noreferrer"&gt;https://bennettfinancials.com/fractional-cfo-services-for-budgeting-and-forecasting-excellence/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connect with Arron on LinkedIn: &lt;a href="https://www.linkedin.com/in/arron-bennett/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/arron-bennett/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Book a consultation: &lt;a href="https://bennettfinancials.com/contact-us/" rel="noopener noreferrer"&gt;https://bennettfinancials.com/contact-us/&lt;/a&gt;&lt;br&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%2Fdhfwp38zfksneg66bz1l.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%2Fdhfwp38zfksneg66bz1l.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>saas</category>
    </item>
  </channel>
</rss>
