by Brandon Rhodes • Home

Choosing your salary under a SEP, SIMPLE, and a Solo 401k

Date: 13 November 2013

If you have elected S-corporation status for your small consultancy and maintain yourself as the only employee, then you have an interesting choice in front of you each year: at what level should you set your own salary?

I used to think that the answer was simple: that the salary should be as low as possible because every additional dollar allocated to salary incurs Social Security and Medicare tax. This sets up a tug-of-war between your annual income — which benefits from as low a declared salary as possible — and your conscience and the tax courts, which both demand that your salary be high enough to compare favorably with others doing the same work out in larger corporations.

But it turns out that once your consultancy has its head above water, and you begin to think about establishing a retirement account, the answer becomes far more interesting. This IPython Notebook explores the issue, and can be downloaded at the following link if you want to experiment with it yourself:


So that plots render inline, we will start by activating the pylab environment:

%pylab inline
pylab.rcParams['figure.figsize'] = (8.0, 6.0)
pylab.rcParams['font.size'] = 12
Populating the interactive namespace from numpy and matplotlib

And before considering any retirement plans, we had better explore our baseline — a small one-person S Corporation — and see why the “take the minimum possible salary” rule applies.

Update (Christmas 2013): I have added a `where()` call to the botton of each plan’s function to make sure that the combined cost of all taxes and contributions will not exhaust the business’s funds.

Personal taxes

Personal income tax is assessed on your “Taxable income” as tallied on Line 43 of your Form 1040 (PDF) and involves mulitplying each successive dollar by the rate of the “tax bracket” into which it falls. Here are the tax brackets for 2013 for the four basic categories of taxpayer, indexed by the abbreviations used in IRS tax form PDF fields:

brackets = {
    'S': [  # Single
        (0, 8925, 0.10),
        (8925, 36250, 0.15),
        (36250, 87850, 0.25),
        (87850, 183250, 0.28),
        (183250, 398350, 0.33),
        (398350, 400000, 0.35),
        (400000, inf, 0.396),
    'MJ': [  # Married filing jointly
        (0, 17850, 0.10),
        (17850, 72500, 0.15),
        (72500, 146400, 0.25),
        (146400, 223050, 0.28),
        (223050, 389350, 0.33),
        (389350, 450000, 0.35),
        (450000, inf, 0.396),
    'MS': [  # Married filing separately
        (0, 8925, 0.10),
        (8925, 36250, 0.15),
        (36250, 73200, 0.25),
        (73200, 111525, 0.28),
        (111525, 199175, 0.33),
        (199175, 225000, 0.35),
        (225000, inf, 0.396),
    'HoH': [  # Head of household
        (0, 12750, 0.10),
        (12750, 48600, 0.15),
        (48600, 125450, 0.25),
        (125450, 203150, 0.28),
        (203150, 398350, 0.33),
        (398350, 425000, 0.35),
        (425000, inf, 0.396),

The tax due on a particular amount of taxable income can be computing by simply looping over the brackets and tallying up the product of each tax rate times the dollars that fall into that bracket:

def compute_income_tax(taxable_income, status):
    "Return the income tax due on `wages`."
    tax = 0.00
    for bottom, top, rate in brackets[status]:
        dollars = clip(taxable_income, bottom, top) - bottom
        tax += rate * dollars
    return tax

As the United States Congress intends, this tax regime produces a much smaller relative tax liability at low income levels than it produces at higher incomes. We can see this if we graph the “bite” that taxes take out of an increasing level of taxable income — the red wedge representing taxes is proportionally more narrow at lower income levels:

income = arange(100.00, 500000.00, 100.00)
income_tax = compute_income_tax(income, 'MJ')

fill_between(income, 0.00, income, color='#8888ff')
fill_between(income, 0.00, income_tax, color='#ff8888')
xlabel('Taxable income')
ylabel('Taxable income (blue), Taxes due (red)')
<matplotlib.text.Text at 0xac3a10c>

This relationship is even clearer if instead of eyeballing the red area of the graph we simply divide the tax liability by the income to produce the “effective” tax rate at each income level: it starts small at 10% and then ramps quickly towards 30% and beyond:

effective_rate = income_tax / income
plot(income, effective_rate)
xlabel('Taxable income')
ylabel('Effective tax rate')
<matplotlib.text.Text at 0xa4f764c>

Finally, we can visualize the progressive tax structure by computing the “marginal” tax rate at which each additional dollar earned is taxed. The NumPy diff() function is perfect for this:

marginal_rate = diff(income_tax)
fill_between(income[1:], marginal_rate)
xlabel('Taxable income')
ylabel('Marginal tax rate upon an additional dollar')
<matplotlib.text.Text at 0xae2fb6c>

Do three graphs of the same tax regime seem repetitive? My purpose in including all three is actually to make sure that the compute_income_tax() function is behaving as expected, before we start building more logic upon it!

Another quantity that we will need is the total exemptions for a given tax filing, which — assuming no children — can be tallied quite easily:

def compute_exemptions(status):
    n = 2 if status.startswith('M') else 1
    return 3800.00 * n

Finally, we need the current rates at which income is taxed for the Social Security and Medicare taxes, a pair of taxes that are levied against every employee in the United States. Unlike personal income taxes, these two taxes cannot be disputed or adjusted or reduced; there is no equivalent to “Form 1040” for Social Security or Medicare through which an employee can get a refund at the end of the year!

employer_social_security_rate = 0.062
employer_medicare_rate = 0.0145

employee_social_security_rate = 0.062
employee_medicare_rate = 0.0145

social_security_wage_base = 113700.00

With these numbers defined, we are ready to proceed.

How S-Corporation Taxes Work

My own filing status is “married filing jointly” but since you might later want to run this IPython Notebook against your own circumstances, I am defining it here so that you can change it in one place:

# Choose a status from "brackets" above
my_status = 'MJ'

Now, how do the earnings of your S Corporation reach your pocketbook?

The earnings reach you in two different ways:

  1. As a salary paid to you as monthly wages.
  2. Any remaining profit becomes supplemental income at year's end.

(Note that I take the term “supplemental income” from the title of Form 1040 Schedule E where, in Part II, your S Corporation’s “Ordinary business income” becomes part of your personal income.)

This distinction, it turns out, carries moral import. You deserve your salary thanks to the labor that you perform on behalf of the S Corporation, but you earn your supplemental income as the investor who started and capitalized your business and took the risk that it would not succeed.

Until you have established your salary level, the “extra” monies that will be produced by your S Corporation this year are freely available to become either salary or supplemental income. Since there does not seem to be a formal technical term for these monies before they are so divided, I will have to invent an informal one for the convenience of this notebook:

Gravy — as in the phrase “everything else is gravy,” we here use the term to mean “everything else” left over when all other costs of running our S Corporation have been accounted for. Defined more precisely: gravy is the total on Line 21 “Ordinary business income” of Form 1120S if we set Line 7 “Compensation of Officers” to zero, set Line 17 “Pension, profit-sharing, etc.” to zero, and include zero Social Security or Medicare taxes related to officer compensation on Line 12 “Taxes and licenses.”

With this term defined, the stark choice that faces a one-person S Corporation can be phrased as, “How much of this year's gravy should I pay myself as a salary?”

The answer has important tax consequences. Both salary and supplemental income wind up on 1040 line 43 as taxable income, and are to that extent interchangable: they will make you hit all of the same tax brackets that we saw above as they add up to your total gravy. But each dollar of salary has two additional liabilities that a supplemental income dollar does not: it first incurs Social Security and Medicare taxes for the business, and then incurs Social Security and Medicare taxes again for the individual that are withheld as shown on Form W-2!

We can explore this trade-off through the following function. It assumes that you have already completed Form 1120S to include all other business expenses besides your salary, and computes the extra taxes that you will incur by taking a given number of dollars of your gravy and designating them as wages instead of letting them emerge as supplemental income:

def tax_plain(gravy, wages, status):
    ss_wages = minimum(wages, social_security_wage_base)

    # s = Form 1120S,
    # "U.S. Income Tax Return for an S Corporation"

    s7 = wages
    s12 = (ss_wages * employer_social_security_rate
            + wages * employer_medicare_rate)
    s21 = gravy - s7 - s12
    supplemental_income = s21

    # w = Form W-2,
    # "Wage and Tax Statement"

    w4 = ss_wages * employee_social_security_rate
    w6 =    wages * employee_medicare_rate

    # i = Form 1040,
    # "U.S. Individual Income Tax Return"

    i7 = wages
    i17 = supplemental_income
    taxable_income = i7 + i17 - compute_exemptions(status)
    income_tax = compute_income_tax(taxable_income, status)

    total_tax = s12 + w4 + w6 + income_tax
    return where(supplemental_income >= 0, total_tax, nan)

If you trace through this function imagining wages to be zero, then you will discover the best possible scenario tax-wise: all of the gravy becomes supplemental income without being whittled down by extra business or personal taxes, and only gets taxed as “Taxable income.”

The alternative is worse. When wages is a value greater than zero, the business has to pay Social Security and Medicare taxes both on its own behalf (which are reflected as an additional business tax expense on Form 1120S line 12), and also on the employee’s behalf (as reflected on the W-2 given to the employee at year’s end). These result in additional tax payments that are included in the return value.

Note that you can never actually ever set wages equal to your gravy — at least not if you want your business to continue as a going concern! — because the business must have enough cash left over after disbursing payroll to pay its own share of the Social Security and Medicare taxes. That is why the last line of the above function is careful to use where() to avoid returning data when the supplemental income has finally been driven negative, so that nothing will show up on the following graphs at salary levels high enough to drive you out of business. (The NumPy value nan is the IEEE Floating Point value “not a number” which omits that data point from our graphs.)

To make the situation concrete with a plot, let’s imagine several single-employee S Corporations, the smallest of which will only clear $25,000 in gravy this year but the largest and most extravagant of which will earn a full $300,000 in gravy.

Each owner of one of these hypothetical businesses faces a question: how much of the gravy should become salary, and how much may remain to become supplemental income? If each business owner considers the full range of answers to this question, from a zero salary up to a salary that consumes every gravy dollar available, then they will be contemplating a range of tax liabilities that can be shown as a line on a graph:

def graph_businesses(plan, *args):
    gravies = arange(25000.00, 300001.00, 25000.00)
    for gravy in reversed(gravies):
        wages = arange(0.00, gravy, 1000.00)
        plot(wages, plan(gravy, wages, my_status, *args),
             label='$%dk' % int(gravy // 1000))
    xlabel('Gravy paid as wages')
    ylabel('Tax incurred')
    legend(loc='center left', bbox_to_anchor=(1, 0.5))

Consider the smallest business: the tiny $25,000-a-year consultancy whose range of options is depicted as the short purple line at the bottom of the chart. If the owner pays themselves no salary — the point represented by the left end of the line — then their tax liability will be as small as possible. But if they instead take their entire business income as salary, all $25,000 of it, then they position themselves on the far-right edge of their purple line and — as shown by that point’s vertical position — they will pay considerably more in tax. How much more?

print tax_plain(25000.00, 0.00, my_status)
print tax_plain(25000.00, 20000.00, my_status)

By incurring Social Security and Medicare taxes on their meagre income, this business owner has more than doubled their tax liability!

The extravagant $300,000-a-year consultancy, on the other hand, faces a less dire choice, as you can see from the blue line near the top of the graph which traces out its various options. Their effective income tax rate that they must pay on both salary and supplemental income will be somewhere north of 20%, as we can see in our earlier graphs, so the extra Social Security and Medicare taxes make a smaller relative difference to their tax burden. The minimum and maximum taxes they can pay depending on the salary chosen are:

print tax_plain(300000.00, 0.00, my_status)
print tax_plain(300000.00, 288000.00, my_status)

# Verify that we cannot go higher:
print tax_plain(300000.00, 289000.00, my_status)

This is a smaller relative difference, but nineteen thousand dollars is still a lot of money!

An interesting take-away from this graph is the dog-leg that the lines of the larger consultancies make once their chosen salary exceeds $113,700. This is the value that we earlier named `social_security_wage_base` and it is a very interesting feature of the tax landscape: while the Medicare taxes for which you are liable tend towards infinity as your salary grows without bound, your Social Security liability ends abruptly and all higher salary levels incur only the tax on a plain old $113,700 salary no matter how much more than that you make.

But the most important observation to make is that our original generalization is true: if tax liability is the only thing you are considering, then you should take the smallest salary possible. Every dollar in additional salary hurts the final tax outcome.

This inflection point in the graph will become very interesting as we now explore some options for our retirement account!

The SIMPLE retirement account

If you open a SIMPLE retirement account, then as an employee you can contribute up to $12,000 each year. Each dollar contributed counts as a reduction in your taxable salary, and disappears entirely from your final tally of taxable income!

The S corporation employer is expected to either match that contribution up to a maximum match of 3% of the total salary, or choose to make a set contribution to the SIMPLE account that equals 2% of the employee’s salary. The difference is small, but we will account for both with a do_match Boolean argument to our tax_simple() function that determines our tax liability if we have decided to contribute the maximum amount that we possibly can to our SIMPLE retirement account:

def tax_simple(gravy, wages, status, do_match):
    ss_wages = minimum(wages, social_security_wage_base)

    # SIMPLE contribution limits

    employee_contribution = minimum(wages, 12000.00)

    if do_match:
        employer_contribution = minimum(
            wages * 0.03, employee_contribution)
        employer_contribution = wages * 0.02

    # s = Form 1120S,
    # "U.S. Income Tax Return for an S Corporation"

    s7 = wages
    s12 = (ss_wages * employer_social_security_rate
            + wages * employer_medicare_rate)
    s17 = employer_contribution
    s21 = gravy - s7 - s12 - s17
    supplemental_income = s21

    # w = Form W-2,
    # "Wage and Tax Statement"

    w4 = ss_wages * employee_social_security_rate
    w6 = wages * employee_medicare_rate

    # i = Form 1040,
    # "U.S. Individual Income Tax Return"

    i7 = wages - employee_contribution
    i17 = supplemental_income
    taxable_income = i7 + i17 - compute_exemptions(status)
    income_tax = compute_income_tax(taxable_income, status)

    total_tax = s12 + w4 + w6 + income_tax
    return where(supplemental_income >= 0, total_tax, nan)

Note that income disappears in two places here. The employer contribution towards retirement disappears from Form 1120S thanks to being counted as an expense on line 17, and the employee dollars earmarked for retirement disappear from Form 1040 line 7 thanks to the fact that they are deduced directly on the W-2 issued by the S Corporation.

The result is a bit more interesting that the normal situation when it comes time to choose your salary level:

graph_businesses(tax_simple, True)

For very small income levels — the S corporation making $25,000 in gravy — the possiblity of stashing money away in a SIMPLE account merely cushions the tax consequences of the first $10,000 in salary, flattening out the left edge of each curve.

But turning our attention to the upper six curves, for S corporations with at least $100,000 in gravy to split between salary and supplemental income, we get a pleasant surprise: the total tax liability actually *falls* if they choose to pay $12,000 in salary instead of zero!

This is because the marginal tax rate at these income levels is so large that the expense of paying a bit of Social Security and Medicare tax is more than offset by the disappearance of entire dollars — and thus of the income tax that they incur — into your retirement account.

But, of course, the effect stops once the salary reaches $12,000 since at that point the SIMPLE contribution limit has been reached. Every dollar from that point onward that is allocated as salary will incur its usual rate of tax, with only a slight offset from the fact that the employer is getting to contribute 3¢ of that dollar as the employer’s share of the SIMPLE contribution.

One last issue: note that these curves do not extend as far to the right as the curves on our first graph. This is because the employer contribution to the SIMPLE account creates an extra burden on the business balance sheet, and drives the supplemental income negative even earlier than would the combination of salary and payroll taxes alone.

The SEP retirement account

The SEP plan is more straightforward. It involves no employee contribution, and thus provides no chance to make wages disappear from Form W-2. Instead it is entirely employer-funded, with a maximum contribution of one-quarter of the employee's wages up to a contribution limit of $51,000 per year.

def tax_sep(gravy, wages, status):
    ss_wages = minimum(wages, social_security_wage_base)

    # SEP contribution limits

    sep_wages = minimum(wages, 255000.00)
    sep = minimum(sep_wages * 0.25, 51000.00)

    # s = Form 1120S,
    # "U.S. Income Tax Return for an S Corporation"

    s7 = wages
    s12 = (ss_wages * employer_social_security_rate
            + wages * employer_medicare_rate)
    s17 = sep
    s21 = gravy - s7 - s12 - s17
    supplemental_income = s21

    # w = Form W-2,
    # "Wage and Tax Statement"

    w4 = ss_wages * employee_social_security_rate
    w6 = wages * employee_medicare_rate

    # i = Form 1040,
    # "U.S. Individual Income Tax Return"

    i7 = wages
    i17 = supplemental_income
    taxable_income = i7 + i17 - compute_exemptions(status)
    income_tax = compute_income_tax(taxable_income, status)

    total_tax = s12 + w4 + w6 + income_tax
    return where(supplemental_income >= 0, total_tax, nan)

Obviously the benefits of this arrangement grow more slowly with increasing salary than did the benefits of the SIMPLE account. With the SIMPLE plan, you could contribute 100% of your first $12,000 of salary directly into your retirement account, making your first $12,000 in income simply disappear for tax purposes. But with the SEP plan, only 25¢ of each additional dollar of salary can disappear from Form 1120S line 17 and into your retirement account. This sounds like a losing proposition: won’t it still be worth it to set our salary as low as possible?


Oh, wow!

For small S Corporations making around $150,000 or less, our previous guidelines apply: the lower you can set your salary, the better the tax consequences. But if you can set your salary level above about $120,000 then something magic happens: you actually start paying less tax with each additional dollar that you allocate to salary!

What is going on?

The answer lies in the inflection point that we have been noticing ever since our very first graph of tax consequences: once we reach the Social Security Base Wage of $113,700, each additional dollar of salary incurs only the two Medicare taxes. And it turns out that this expense is slightly less than the savings generated when the employer donates a further 25¢ toward our retirement and spares that 25¢ from being taxed as supplemental income at our (high) tax bracket! Observe:

# Increasing salary from $150,000 to $150,001
# incurs both employer and employee Medicare tax
# and costs us nearly 3 cents...

print 1.00 * (0.0145 + 0.0145)

# ...but that extra dollar lets us put 25 cents
# more towards retirement, saving more than
# 8 cents in taxes as a consequence!

print - 0.25 * (0.33)
print 'Savings per $ of salary:', 0.029 - 0.0825
Savings per $ of salary: -0.0535

But why do these salary lines end at much lower salaries than the ones we were graphing above? The $300k S-Corporation in this new graph cannot even muster a salary of $250k, which is much lower than the limit encountered before! The reason is that the SEP contribution is taken entirely from the business bank account, not from your salary as employee, and thus drives the supplemental income negative — and, with it, the balance left in your business checking account come year-end — much more quickly than does a retirement plan whose payments are made from your personal bank account.

Of course, your business could survive higher salary payments by cutting back on your retirement contributions, depositing less than the full 25% that the SEP allows. But at that point tax liability would move sharply upwards since each additional dollar of salary would actually decrease how much you were putting into retirement, so I am allowing each line on the graph to simply end once the full SEP contribution can no longer be maintained.

And high in the graph’s stratosphere, at the right end of the lines for the very high earners, you can see a more shallow inflection point where the SEP contribution reaches its limit: the salary has reached $204,000 and has brought the SEP contribution to is maximum possible value of $51,000 so no further increases in salary will increase your retirement benefit.

The shape of these curves leads to very interesting strategy questions if the S Corporation’s income ever starts inching north of $150,000. At all lower income levels the best strategy was to take the smallest salary one could reasonably justify, and be prepared to defend it in tax court. But once this higher range has been reached, it probably makes sense to simply take all income as salary and thus to reduce the total tax liability to a level that would otherwise need a very low salary to match!

As a concrete example, let us consider $200,000 in annual gravy with a SEP retirement account. The tax liability on the maximum possible salary is:

from scipy.optimize import fminbound

def g(wages):
    return tax_sep(200000.00, wages, my_status)

x1 = fminbound(g, 100000.00, 200000.00)

print 'Local minimum at:', x1
print 'Tax liability:', g(x1)
Local minimum at: 152590.429895
Tax liability: 46629.0302901

How far to the left would we have to go along the curve to reach the crest and then travel down its left side until finding another salary level with this low a tax burden?

Once again SciPy makes the task easy:

from scipy.optimize import newton

def geq(wages):
    return g(wages) - g(x1)

x2 = newton(geq, 50000.00)

print x2

Let this fact sink in! If the consultant with business income of $200,000 decides to drop their salary below $152,000 then they will have to jump all the way down to $86,000 — cutting their salary by almost half — before they reach a point of equivalent tax advantage. Here are those two points we just computed, to make sure the idea is clear:

x = arange(1000.00, 200001.00, 1000.00)
plot(x, g(x))
scatter([x1, x2], [g(x1), g(x2)])
plot([x1, x1, x2, x2], [0, g(x1), g(x2), 0], linestyle='--')
xlabel('Gravy paid as wages')
ylabel('Tax incurred')
xlim(0.00, 200000.00), ylim(0.00)
((0.0, 200000.0), (0.0, 60000.0))

So at high enough consultancy income levels, the mantra of “always choose the lowest salary you can justify” is simply false in the presence of an aggressive SEP retirement scheme, and can actually hurt you financially if your chosen salary lands near the local maximum where, if only you were bold enough to pay yourself more, the social security tax would finally cut out and let the SEP contribution start whittling away at your taxes.

The Solo 401k

There are many reasons not to set up a 401k as the sole employee of your S Corporation — forms and expenses and fees and all that — but for a moment let us just examine the tax consequences of being able to put away up to $17,500 as the employee, while as the employer being able to stash away an amount equal to 25% of the employee’s salary. The entire amount contributed by both the employer and employee cannot exceed the same $51,000 limit for 2013 that haunted our SEP account above.

def tax_solo_401k(gravy, wages, status):
    ss_wages = minimum(wages, social_security_wage_base)

    # 401k contribution limits

    deferrals = minimum(wages, 17500.00)
    contributions = minimum(51000.00 - deferrals, wages * 0.25)

    # s = Form 1120S,
    # "U.S. Income Tax Return for an S Corporation"

    s7 = wages
    s12 = (ss_wages * employer_social_security_rate
            + wages * employer_medicare_rate)
    s17 = contributions
    s21 = gravy - s7 - s12 - s17
    supplemental_income = s21

    # w = Form W-2,
    # "Wage and Tax Statement"

    w4 = ss_wages * employee_social_security_rate
    w6 = wages * employee_medicare_rate

    # i = Form 1040,
    # "U.S. Individual Income Tax Return"

    i7 = wages - deferrals
    i17 = supplemental_income
    taxable_income = i7 + i17 - compute_exemptions(status)
    income_tax = compute_income_tax(taxable_income, status)

    total_tax = s12 + w4 + w6 + income_tax
    return where(supplemental_income >= 0, total_tax, nan)

This more or less combines the features of both of the investment account types we have considered so far. Like a SIMPLE account, you get to defer your first several thousand dollars of salary directly into the retirement account, giving that sharp downward trend to the left edge of the chart — declaring at least a small salary is an immediate tax benefit. Then, for bigger earners, there is a salary amount up around $130,000 where additional dollars again earn a tax benefit, creating a local minimum.

The biggest difference will be apparent when we now proceed to compare all four situations at once!

Comparing investment accounts

We now have the tools to answer the big question: which investment account gives the greatest tax advantage for the consultant incorporated as an S Corporation, given that each dollar of income can be apportioned as either salary or as supplemental income by adjusting one’s monthly wages?

The following function compares the plans for an S Corporation earning a given amount of gravy.

def compare_plans(gravy):
    wages = arange(0.00, gravy, 1000.00)
    plot(wages, tax_plain(gravy, wages, my_status),
    plot(wages, tax_sep(gravy, wages, my_status),
    plot(wages, tax_simple(gravy, wages, my_status, True),
         label='SIMPLE (matching)')
    plot(wages, tax_simple(gravy, wages, my_status, False),
         label='SIMPLE (nonelective)')
    plot(wages, tax_solo_401k(gravy, wages, my_status),
         label='Solo 401k')
    xlabel('Gravy paid as wages')
    ylabel('Tax incurred')
    legend(loc='center left', bbox_to_anchor=(1, 0.5))

If you download this IPython notebook then you can try running this function yourself for varying amounts of gravy. But you will find that the total gravy mostly moves the same graph up and down by varying the total income tax level. The shape of the graph is always roughly the same, and to see all of its characteristics it will be best to extend the graph all the way out to three hundred thousand dollars:


There you have it:

And in all cases, be sure to construct a graph showing all of the possible salary levels that you could choose — surprisingly enough, sometimes a higher salary will actually reduce your tax burden by letting you put away more for retirement.

A few final notes are in order:

Please feel free to offer any corrections or improvements, and if appropriate I will incorporate them as changes into this blog post. Thanks!