Steve Jobs famously cared about the unseen backs of cabinets. Antique furniture built with hand tools isn’t like that at all. Cabinetmakers made each part to the tolerance that mattered. The invisible parts were left rough, with plane and saw marks, to save time. The visible parts, however, were cleaned up and polished. Some surfaces were made precisely straight and square, for structural reasons; while nonstructural surfaces were only straight enough to look good to the eye.
Think about an apprentice in an old cabinet shop. An apprentice painstakingly smoothing an invisible surface would be yelled at for wasting time. An apprentice failing to smooth a visible surface would be yelled at for producing crappy work. To become a professional, the apprentice learned to work efficiently but still do a good job. Crucially, a “good job” was defined in terms of customer concerns. 
Cabinetmakers were focused on what their customers cared about. Customers wanted the furniture to look good, and they wanted it to be structurally sound. They didn’t care about invisible tool marks, and didn’t want to pay extra to have those removed.
Software remains a craft rather than a science, relying on the experience of the craftsperson. Like cabinetmakers, we proceed one step at a time, making judgments about what’s important and what isn’t at each step.
A professional developer does thorough work when it matters, and cuts irrelevant corners that aren’t worth wasting time on. Extremely productive developers don’t have supernatural coding skills; their secret is to write only the code that matters.
How can we do a better job cutting corners? I think we can learn a lot from people building tables and dressers.
1. Own the implementation decisions
It is irresponsible to ask the customer (or manager, or other not-doing-the-work stakeholder) to tell you how to make technical tradeoffs. Cabinetmakers didn’t ask their customers how flat a tenon had to be and this is not the customer’s problem. The customer wants us to do it properly but not wastefully. It is our decision how to go about this, and if we get it wrong it’s our fault.
On software teams, there’s often a developer trying to push these decisions up to management or onto the customer, because they don’t want to “get in trouble” later. Perhaps they complain to management about “technical debt” and being “given time to work on it.” This is a sign that we aren’t owning our decisions. If the technical debt is a problem, 1) we shouldn’t have put it in there, and 2) we should include it in our estimates and address it. A cabinetmaker would not ask the customer to put “make tenons straight” on the sprint. Nobody cares. Technical debt is our problem; that’s the job.
If you don’t own your technical decisions, you can never get them right, because nobody else knows how to make them. Insist on making them. And yes, that means getting them wrong is your fault. It may mean giving people bad news about how long things will take. It may mean you get yelled at sometimes.
2. Understand the customer’s needs and preferences
Because we must make tradeoffs and not push choices onto the customer, we have to understand what matters and what doesn’t. It’s easier to establish this in the world of furniture (“doesn’t break when you sit on it,” “looks nice”). In software, we have to know what job our software will do for the customer.
This is where we should be getting customer input (though watching what they do may be more valuable than asking them what they think), and reaching a consensus with our management team or client.
We should not ask customers for more precision than they can give us (a symptom of this is to badger customers or managers for detailed “requirements,” then complain endlessly about “changing requirements”). Our job involves converting vague needs into concrete software — if we’re lucky, we have the help of a skilled product designer, or guidance from a management team that’s able to be somewhat precise, but if not we have to do it ourselves. Accept the job and learn to do it.
It’s unprofessional to be the kind of developer who doesn’t care about user experience, doesn’t care about business context, or “just wants to be told the requirements.” It’s impossible to work efficiently or to do a good job without understanding the context.
A professional developer can take a desired UX and work out the technical steps to get there as efficiently as possible. And they do get there; they don’t build something odd that doesn’t meet the need, or something slapdash that doesn’t work.
3. Don’t be lazy
Corner-cutting should be a deliberate decision; “this truly doesn’t matter.” It should not be because it’s 5pm and we’re going home. When we find ourselves asking “do I really have to redo this…” then we need to redo it.
Cutting corners should feel like you have a clear focus and you’re skipping tasks that don’t matter for that focus. Cutting corners should not feel like you’re doing poor-quality work.
To push back on an unrealistic schedule, work to narrow the scope or weaken the requirements.
Let’s say you’re making some kitchen cabinets. You could make them all with hand tools, no metal connectors and no plywood. They would be gorgeous and cost about $150,000. When the customer says that’s too much time and too expensive, you could make them the usual modern way with machines, screws and plywood; which is a sound approach, though a little uglier. This is like offering to build a web app that’s not quite as slick and beautiful — something a little more off-the-shelf.
That’s all fine. What’s not fine: delivering either of those choices unfinished and broken. “Oh, I forgot the cabinet doors.” “Sorry these things aren’t painted!”
To cut scope, we should do something defined (such as leave out a feature or refinement), rather than something undefined (like skipping testing).
Professionals are doing it for others
All of this sounds hard, and it is. As in Amy Hoy’s description of these students learning a craft, at first we may fight it and focus on our own needs and emotions.
Professional software developers are performing a service for others. That’s the difference between a professional and a hobbyist or an artist. To perform a service for others, we have to know what others need, and apply our expertise to meet those needs.
 Furniture made by machine doesn’t have the same ability to flex tolerances to save time. For the most part, with woodworking machines you get what you get; the machine doesn’t know whether your surface will be visible, or how flat it has to be. It makes one kind of surface and that’s it. Some parts of machine-made furniture aren’t as good as a handmade joint or surface could be, while other parts are far more precise than necessary. Check out this discussion of how to cut a dado joint by hand, which mentions several ways to save time on the back, non-show side of the piece.