What “Hardware is Hard” Really Means

I was chatting with a new hardware entrepreneur recently who asked an excellent question: What does “Hardware is hard” really mean?  It being time consuming and expensive to iterate on, not being able to ship patches, requiring specialized skillsets, and having access to a smaller pool of investors are all reasons commonly mentioned.  These all relate, but at its core, what makes hardware hard is that you don’t get second chances.

Pick the wrong audience target, go to market strategy, feature set, pricing, production forecast, manufacturing methodology, manufacturing partners, or logistics partners, and you will run out of money.  Worse, you can get all but one of those things right and still never make it off the ground.  Startups require setting a bold hypothesis and then charting a narrow course to proving it.  In many software and service businesses, you can set short milestones to test and iterate with target audiences closed-loop, course correcting rapidly to find product market fit with a small team before scaling.  If you have a strong team and you’re picking good hypotheses, you’ll have a reasonable chance to get enough funding to keep going at it for some time.

With hardware, each of those is orders of magnitude more challenging.  Iteration cycles are expensive, partner and technology selections can take you through one way doors you can’t afford to walk back from, and building inventory of the wrong product or for the wrong audience is fatal.  Perhaps most critically, if you run out of money, you are unlikely to get funding to pivot, no matter how strong your team is.

All of that said, hardware is essential to civilization and a whole lot of fun to make.  Nobody needs another Web3 NFT marketplace.  Build something that matters.  Here’s what you can do to minimize your likelihood of death along the way:

  1. Be extremely deliberate about your audience sequencing.  Think about the first 1k, the first 10k, the first 100k, and the first 1M customers.  Don’t start your company with a product or go to market strategy for the first 1M customers unless you are certain that it is what will also satisfy the first 1k.  It takes brand awareness, company credibility, and ultimately the right product to fit your audiences’ needs to reach each subsequent level of scale.  There are ways to streamline this that will depend on your product and category.  For example, starting with an openly available development kit works well for products that will eventually have a hardware or software ecosystem attached to them (something we did successfully at Oculus).
  2. Deeply understand how your audiences behave today, what they are buying, and why what you are building would be enough better to make them change their behavior.  Think through the purchase cycle.  Does this need to be a replacement, a first product, a second product, or a toy to tinker with?  I don’t generally subscribe to product/management fads, but Jobs-to-be-done has a useful framing for audience psychology with the “four forces” diagram.
  3. Build the right team and pick the right partners.  Many hardware startups try to emulate Apple’s product and design philosophy and then replicate the staffing model that requires.  Don’t.  Start with the smallest possible team for what you need to differentiate on in design and engineering along with the core set of people needed to safely steer supply chain and logistics.  Leverage partners for everything else.  You can always in-house more in the future as you scale, but don’t make the mistake of building substantial capital infrastructure or hiring team members that you don’t need to to reach product market fit.  You can solve this with a competent manufacturing partner that has expertise or at least transferable skills and infrastructure for your space. You can work with them in a “Joint Development Model”.  The people you’ll work with there will be carrying in an enormous amount of collective experience and team cohesion, won’t need equity, can move fast, and will generally require less OpEx than hiring the equivalent capability in house.
  4. Think carefully about funding and runway.  Unless you can afford to self-fund your company to profitability, make sure you’re actively having discussions with potential investors as you plan, whether angels or institutional VCs.  Lay out your hypotheses around product roadmap and audience scaling and the funding milestones that go with them.  Critically, make sure plausible investors agree that those are sensible milestones, otherwise you’ll end up at one without the cash to go further.
  5. Find ways to get the first 1k or 10k customers to help fund the business.  Never use equity to fund inventory.  Think carefully about how you can generate enough credibility to get 1k or 10k people to put money down on a product that hasn’t been manufactured yet.  That is most often in the form of pre-orders.  Credibility generation may be through seeding development units to influential users or press in the space, though a limited developer kit program, through a community beta program, or other creative paths.  If you can’t get 1k customers to take the bet and spend money on your product, investors probably shouldn’t place a bet on you either.

This is by no means a comprehensive list of tactics, but a condensed set of viewpoints around a topic that could fill a book.  Note that all of the numbers above assume a consumer hardware product in a large category.  Feel free to scale the numbers appropriately for enterprise/commercial/niche hardware.  If you’re starting a hardware startup, especially in consumer, and you’d like feedback on how you’re approaching it, feel free to send me an email.  I applied each of the above to Framework, and would be happy to help check for startup-advice-fit on other companies.

No Comments on What “Hardware is Hard” Really Means

Parametric M.2 Mockup Generator

The thing I’m sharing today is a very early and very small slice of the project that is likely going to consume the next few years of my life. This is a parametric generator for M.2 expansion card mockups in OpenSCAD.

I can’t imagine this is useful to many people, but the beauty of open source software and hardware is that it doesn’t have to be. Even if only one person happens upon this years down the road and it saves them a few hours or gives them the reference point they needed, it was worth sharing. Sharing knowledge isn’t zero-sum; it’s exponential (though maybe not in this specific instance).

// M.2 Mockup Generator
// by Nirav Patel <https://eclecti.cc>
//
// To the extent possible under law, Nirav Patel has waived all copyright
// and related or neighboring rights to M.2 Mockup Generator.
//
// An easy to use parametric M.2 mockup generator.
// See https://en.wikipedia.org/wiki/M.2 for more information about the 
// M.2 (formerly NGFF) standard.

edge_width = 19.85;
edge_length = 4;
edge_r = 0.5;
hole_r = 1.75;
key_r = 0.6;
key_length = 3.5;

pcb_thick = 0.8;
bottom_edge_keepout = 5.2;
top_gnd_pad_r = 2.75;
bottom_gnd_pad_r = 3;
bottom_gnd_pad_offset = 1;
bevel = 0.3;
bevel_angle = 20;

$fn=50;

module key_cutout() {
    union() {
        translate([0, key_length - key_r]) circle(r = key_r);
        translate([-key_r, -1]) square([key_r*2,key_length - key_r +1]);
    }
}

function get_key(key_type="mm") =
    key_table[search([key_type],key_table,1,0)[0]][1];

key_table = [["", 100], ["A", 6.625], ["B", 5.625], ["C", 4.625], ["D", 3.625], ["E", 2.625], ["F", 1.625], ["G", -1.125], ["H", -2.125], ["J", -3.125], ["K", -4.125], ["L", -5.125], ["M", -6.125]];

module m2_2d(width=22, length=30, key="M", key2="") {
    // Start with a 2d version
    difference() {
        square([width, length]);
        
        // Left cutout
        translate([(width-edge_width)/2-width, -edge_r]) square([width, edge_length]);
        translate([(width-edge_width)/2-width-edge_r, 0]) square([width, edge_length]);
        translate([(width-edge_width)/2-edge_r, edge_length-edge_r]) circle(r=edge_r);
        
        // Right cutout
        translate([width-(width-edge_width)/2, -edge_r]) square([width, edge_length]);
        translate([width-(width-edge_width)/2+edge_r, 0]) square([width, edge_length]);
        translate([width-(width-edge_width)/2+edge_r, edge_length-edge_r]) circle(r=edge_r);
        
        // Hole cutout
        translate([width/2, length]) circle(r = hole_r);
        
        // Key cutouts
        translate([width/2 + get_key(key), 0])
            key_cutout();
        
        translate([width/2 + get_key(key2), 0])
            key_cutout();
    }
}

module m2_text(width=22, length=30, height="", key="", key2="") {
    if (key2 != "") {
        string = str(width, length, "-", height, "-", key, "-", key2);
        linear_extrude(height = 1)
            text(string, size = 2.5);
    } else {
        string = str(width, length, "-", height, "-", key);
        linear_extrude(height = 1)
            text(string, size = 2.5);
    }
}

module m2(width=22, length=30, height="", key="", key2="") {
    union() {
        difference() {
            linear_extrude(height = pcb_thick)
                m2_2d(width, length, key, key2);
            
            // The card edge bevel
            translate([0, bevel, 0]) rotate([180-bevel_angle, 0, 0]) cube([width, 10, 10]);
            translate([0, bevel, pcb_thick]) rotate([90+bevel_angle, 0, 0]) cube([width, 10, 10]);
        }
        
        difference() {
            // The various component keepout zones and embossed text
            if (height == "S1") {
                difference() {
                    translate([0, edge_length, pcb_thick]) cube([width, length-edge_length, 1.2]);
                    translate([1, edge_length+1, pcb_thick+1.2-0.5]) m2_text(width, length, height, key, key2);
                }
            } else if (height == "S2") {
                difference() {
                    translate([0, edge_length, pcb_thick]) cube([width, length-edge_length, 1.35]);
                    translate([1, edge_length+1, pcb_thick+1.35-0.5]) m2_text(width, length, height, key, key2);
                }
            } else if (height == "S3") {
                difference() {
                    translate([0, edge_length, pcb_thick]) cube([width, length-edge_length, 1.5]);
                    translate([1, edge_length+1, pcb_thick+1.5-0.5]) m2_text(width, length, height, key, key2);
                }
            } else if (height == "D1") {
                difference() {
                    translate([0, edge_length, pcb_thick]) cube([width, length-edge_length, 1.2]);
                    translate([1, edge_length+1, pcb_thick+1.2-0.5]) m2_text(width, length, height, key, key2);
                }
                translate([0, bottom_edge_keepout, -1.35]) cube([width, length-bottom_edge_keepout, 1.35]);
            } else if (height == "D2") {
                difference() {
                    translate([0, edge_length, pcb_thick]) cube([width, length-edge_length, 1.35]);
                    translate([1, edge_length+1, pcb_thick+1.35-0.5]) m2_text(width, length, height, key, key2);
                }
                translate([0, bottom_edge_keepout, -1.35]) cube([width, length-bottom_edge_keepout, 1.35]);
            } else if (height == "D3") {
                difference() {
                    translate([0, edge_length, pcb_thick]) cube([width, length-edge_length, 1.5]);
                    translate([1, edge_length+1, pcb_thick+1.5-0.5]) m2_text(width, length, height, key, key2);
                }
                translate([0, bottom_edge_keepout, -1.35]) cube([width, length-bottom_edge_keepout, 1.35]);
            } else if (height == "D4") {
                difference() {
                    translate([0, edge_length, pcb_thick]) cube([width, length-edge_length, 1.5]);
                    translate([1, edge_length+1, pcb_thick+1.5-0.5]) m2_text(width, length, height, key, key2);
                }
                translate([0, bottom_edge_keepout, -0.7]) cube([width, length-bottom_edge_keepout, 0.7]);
            } else if (height == "D5") {
                difference() {
                    translate([0, edge_length, pcb_thick]) cube([width, length-edge_length, 1.5]);
                    translate([1, edge_length+1, pcb_thick+1.5-0.5]) m2_text(width, length, height, key, key2);
                }
                translate([0, bottom_edge_keepout, -1.5]) cube([width, length-bottom_edge_keepout, 1.5]);
            } else {
                translate([1, edge_length+1, pcb_thick-0.5]) m2_text(width, length, height, key, key2);
            }
            
            // The ground pad keepouts
            translate([width/2, length, pcb_thick]) cylinder(r = top_gnd_pad_r, h = 10);
            translate([width/2, length-bottom_gnd_pad_offset, -10]) {
                cylinder(r = bottom_gnd_pad_r, h = 10);
                translate([-bottom_gnd_pad_r, 0, 0]) cube([bottom_gnd_pad_r*2, bottom_gnd_pad_offset, 10]);
            }
        }
    }
}

m2(width=22, length=30, height="S3", key="A", key2="E");

This is also up on GitHub as a Gist.

No Comments on Parametric M.2 Mockup Generator