Building a Flickr-to-WordPress Importer with Claude Code

Back in 2018, Flickr decided to limit free accounts to 1000 photos, and like many non-professional photographers, I needed a way out. I started using a WordPress site as my replacement for Flickr to save new photos I wanted to share, calling it “A new replacement for Flickr.” Then I thought of building a small project to import my existing Flickr photos to WordPress.

Fast forward 7 years, and I finally picked up this project again – but this time with Claude Code as my coding partner. What started as a personal need became a fascinating experiment in mastering AI-assisted development.

You can check out the complete project on GitHub and see the imported photos in action on my photoblog. It’s pretty satisfying to see all those Flickr memories living in their new WordPress home.

Continue reading “Building a Flickr-to-WordPress Importer with Claude Code”

WooCommerce Payments – Embracing Stricter L-2 Support

As WooCommerce Payments continues to grow, ensuring that we provide a stable, secure, and efficient experience for merchants is a top priority. One area we’re evaluating is the policy around which versions of WordPress we support—specifically, whether to adopt a stricter L-2 policy, meaning official support only for the latest two major WordPress versions. This shift would help streamline development and testing, but it comes with tradeoffs we want to carefully consider.

Why Haven’t We Implemented a Stricter L-2 Policy Yet?

The main reason we’ve held off on enforcing a stricter L-2 policy is to support as many merchants as possible. Some stores don’t update WordPress immediately for a variety of reasons—compatibility concerns, customizations, or just caution around change. Our current “loose” L-2 policy reflects this: we say we support the latest two versions, but in practice, we don’t block older versions from running WooPayments, and our CI still runs tests against them in some cases.

Maintaining this broader support helps minimize friction for merchants and gives them more flexibility in how and when they upgrade.

Challenges If We Do Not Enforce a Stricter L-2

While broad compatibility is beneficial for users, it creates real challenges for development and testing:

  • Increased Maintenance Overhead: Supporting older WordPress versions often means keeping legacy compatibility code around, which increases the complexity of our codebase.
  • Testing Complexity: Different WordPress and PHP combinations require specific versions of PHPUnit. Our CI pipeline has to include hacks to install and configure the right versions, making the system fragile and hard to maintain.
  • Reduced Developer Velocity: Supporting many combinations means slower builds, more bugs slipping through, and more time spent troubleshooting issues that don’t affect the majority of users.
  • Inability to Adopt Modern APIs: We’re often blocked from using newer WordPress features until we drop support for versions that don’t include them.

In short, the more versions we support, the more technical debt we accumulate—and that makes it harder to build new features confidently and efficiently.

How Do We Make the Decision?

To make a responsible choice, data must drive the decision. Specifically, we need to understand how many active merchants are running WooPayments on older versions of WordPress. If that number is small—say, under 1%—then the impact of enforcing a stricter L-2 policy would be minimal.

This data could come from usage tracking, opt-in telemetry, or marketplace stats. The goal is to quantify the tradeoff: how many users would be affected vs. how much complexity we remove by dropping support.

We’re also considering what the test matrix should look like. It’s not just about which versions are supported, but also which ones we validate in CI. A leaner, more focused CI pipeline makes our code more reliable and our releases more predictable.

What We Will Do Next

Our next steps will focus on data gathering and planning:

  1. Collect usage data to see which WordPress versions are most common among WooPayments users.
  2. Define our support baseline based on that data, likely aiming to officially support only the latest two versions.
  3. Simplify our CI strategy to only cover supported versions, removing outdated PHPUnit workarounds.
  4. Introduce runtime checks in WooPayments to block usage on unsupported WordPress versions with helpful error messages.
  5. Communicate the change clearly and in advance so merchants and developers have time to adjust.

This is part of our broader effort to modernize the WooPayments plugin, reduce technical debt, and provide a better experience for everyone—from merchants to developers.

How to Speed Up Uploading Multiple Files via HTTP

A few months ago, I worked on a research task to find out how to improve the speed of multiple HTTP connections from one server (playing a client role) to another server (playing a server role when uploaded files are eventually saved) at the same time. In this specific issue, both servers belong to the same network. That is, we can make any change we want though allocated resource does matter.

After measurement and looking at a few layers, we found out three points to improve this connection:

  1. Implement persisent connections (keep-alive) on the server side, then the client side. If this is not implemented, after uploading a file, the related TCP connection is closed. When the next file is uploaded, a new TCP connection is constructed and this process takes quite a bit of time. It even takes more time than transferring the huge data via our network.
  2. Implement con-currency connections. That is, instead of using a single TCP connection, the client side starts a few connections at the same time, and send data via these connections. We may test and choose the optimal number to optimize the concurrency.
Continue reading “How to Speed Up Uploading Multiple Files via HTTP”

Code Snippets in functions.php May Not Work as Expected

In WordPress development, it is common practice to add custom code to a theme’s functions.php file to extend or adjust site functionality. While this approach often works for straightforward customizations, there are cases where such code does not behave as expected or fails entirely.


Limitations of functions.php

The functions.php file is associated with the currently active theme and is intended primarily for theme-specific logic. While convenient for minor adjustments, its use comes with inherent limitations:

  • It only loads when the active theme is in use.
  • It may execute before required plugins or components are fully loaded.
  • It may not be included at all in non-standard request types, such as AJAX calls.

As a result, code placed in functions.php may behave inconsistently or be ignored altogether, depending on the request context and execution order.


Common Causes of Snippet Failures

1. Load Order and Execution Timing

WordPress core, themes, and plugins all hook into the loading process at different points. If a snippet relies on functionality provided by a plugin but is executed too early (e.g., before that plugin has fully loaded), it may not work properly.

To understand when various actions and filters run, it is helpful to refer to the WordPress Plugin API Action Reference, which outlines the standard execution flow during a typical request lifecycle.

2. AJAX Requests Have a Separate Execution Path

AJAX requests in WordPress are processed via admin-ajax.php and do not necessarily load the same theme-related files used during standard HTTP requests. This can result in functions.php not being executed, or key variables and hooks being unavailable during AJAX processing.

This issue is documented in real-world cases, such as in the Edit Flow plugin issue on GitHub, where code placed in functions.php did not affect AJAX behavior as expected.


Recommended Solutions

To avoid unexpected behavior when customizing WordPress, consider the following approaches:

Use the Correct Hooks and Load Points

Ensure that custom logic is attached to the appropriate WordPress action or filter and that it runs after all necessary dependencies are available. This often involves using later hooks or adjusting the priority to accommodate plugin load order.

Consider AJAX-Specific Requirements

If the intended functionality involves handling AJAX requests, be aware that these requests follow a different execution path. Code required for AJAX processing should be located in files that are guaranteed to load in both standard and AJAX contexts.

Use a Plugin Instead of functions.php

For custom code that interacts with plugins, operates across the entire site, or needs to persist independently of the active theme, it is more appropriate to place the code in a standalone plugin. This ensures consistency regardless of the theme and gives you more control over how and when the code executes.

For critical logic, consider creating a must-use plugin, which is automatically loaded by WordPress before standard plugins and themes.

add_action – WordPress and SICP

I am going through the well-known Structure and Interpretation of Computer Programs book and come across a mention about add_action.

This immediately brought my attention as WordPress has a similar concept called hooks (add_action and add_filter). Section 3.3.4 of the book gave an example of how this concept is deployed, which is simple but brings a lot of flexibility to the system.

Such an enlightened and connected feeling!

Woo Viet 1.5.0 and OnePay Gateways

It has been one year since my previous release 1.4.5 for Woo Viet – WooCommerce for Vietnam. In this version 1.5.0, I focused on two main issues:

I tracked this version with the milestone feature in GitHub.

Continue reading “Woo Viet 1.5.0 and OnePay Gateways”

WordPress: Detect Homepage on Functions.php

Sometimes you may want to write code that triggers a hook in the homepage of your WordPress site. You try using this code:

// Wrong way if you want to use a hook
// DO NOT USE THIS
if ( is_home() || is_front_page() ) {
  // Add a hook (filter/action) here
}

It can not work because these functions can be really triggered in WordPress template files only. More specifically, they can only be triggered after the main WordPress query is run.

A simple workaround looks like this and actually it can apply to any site, not just WordPress:

// Correct way if you want to use a hook
if ( $_SERVER[ 'REQUEST_URI'] === '/' ) {
  // Add a hook (filter/action) here
}