I recently took over a WooCommerce project for a client whose last developer had vanished. The site was old, cluttered with plugins, and the checkout process was a mess. The client had a simple request: add a custom “Delivery Instructions” field to the checkout and save it to the order. Should be simple. An hour, maybe two. Right?
Famous last words. When I dug into their functions.php, it was a total nightmare. A tangled web of hooks, deprecated filters, and anonymous functions with no comments. My gut reaction, the craftsman in me, was to tell the client we needed to burn it all down and refactor the entire checkout. I started mapping it out—a full-blown project to modernize the code, implement a proper class structure, the works. It was beautiful in my head. And it would have taken a week.
But the client didn’t need a masterpiece of modern architecture. They needed a text box. This is the classic developer trap, something I’ve seen junior devs fall into and, if I’m honest, a trap I still have to stop myself from falling into after 14 years. It’s the “love of creation,” as Jason Cohen once put it in a post I still think about, which I found via an old article on carlalexander.ca. We love to build clean, perfect things. But perfect is often the enemy of shipped.
Pragmatic PHP Development: Sidestepping the Refactor Trap
The client’s budget was for a few hours, not a full week. Pitching a massive refactor wasn’t just overkill; it was tone-deaf. It didn’t solve their immediate problem and would have tanked their budget. The pragmatic solution isn’t always the most elegant one, but it’s the one that respects the client’s reality.
So, I threw out my grand refactoring plan. Instead of trying to fix everything, I focused on finding the single, correct point of entry. I needed one hook to add the field and another to save the data. That’s it. No rewriting their entire checkout flow. I just needed to inject my little piece of functionality with surgical precision.
After a bit of digging, the right hooks turned out to be woocommerce_after_order_notes to display the field and woocommerce_checkout_create_order to save the data. Here’s the kicker: the code isn’t revolutionary, but it’s clean, isolated, and it works without disturbing the hornet’s nest of legacy code around it.
/**
* Add the custom field to the checkout page.
*/
add_action( 'woocommerce_after_order_notes', 'bbioon_add_delivery_instructions_field' );
function bbioon_add_delivery_instructions_field( $checkout ) {
woocommerce_form_field( 'delivery_instructions', array(
'type' => 'textarea',
'class' => array('form-row-wide'),
'label' => __('Delivery Instructions'),
'placeholder' => __('Special instructions for the delivery driver.'),
), $checkout->get_value( 'delivery_instructions' ));
}
/**
* Save the custom field to the order meta.
*/
add_action( 'woocommerce_checkout_create_order', 'bbioon_save_delivery_instructions_field', 10, 2 );
function bbioon_save_delivery_instructions_field( $order, $data ) {
if ( ! empty( $data['delivery_instructions'] ) ) {
$order->update_meta_data( '_delivery_instructions', sanitize_textarea_field( $data['delivery_instructions'] ) );
}
}
So, What’s the Point?
The real fix here wasn’t the code. It was the mindset. It’s about recognizing the difference between what is *possible* and what is *necessary*. We’re paid to solve business problems, not to build our personal software cathedrals.
- Identify the actual need: The client needed a text field, not a new checkout system.
- Find the path of least resistance: Work *with* the system, even if it’s messy. Find the right hook or filter to get the job done without breaking everything else.
- Ship the solution: A working feature delivered on time is infinitely better than a “perfect” refactor that never gets finished.
Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
Could that checkout still use a refactor? Absolutely. And I’ve made a note to recommend it to the client for a future phase. But for now? We solved the problem. We shipped. And that’s the win.
Leave a Reply