Author: Robert

  • More Cursor Programming in WordPress

    I just spent about thirty minutes creating a couple of functions. I can’t really say I wrote them; it was almost entirely done by AI. I think it’s a nice example of ways in which Cursor and AI can really shine, so I thought I’d spend a couple minutes walking through how it went.

    PeakZebra (the system, not the company) has a SQL table that stores data about each of the other SQL tables that PeakZebra creates and uses. The format (for better or worse, and I suspect it’s probably worse) is that the string you’d hand off to the dbDelta function is stored in its entirety.

    The table of tables table

    While initially building PeakZebra, this made it easy for me to quickly change the fields in any of the (now rather numerous) tables I use. I’d just jump into a PHP file where each of these strings was added to the pz_table_str table. An example looks like this;

    // interaction table
    $table_name = $wpdb->prefix . "pz_interaction";
    
    // interaction table
    $item = array();
    $item['id'] = null;
    $item['table_name'] = 'pz_interaction';
    $item['field_string'] = "CREATE TABLE $table_name (
      id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      per_id bigint(20) NOT NULL DEFAULT 1,
      summary varchar(255) NOT NULL DEFAULT '',
      details varchar(800) NOT NULL DEFAULT '',
      created varchar(12) NOT NULL DEFAULT '',
      PRIMARY KEY  (id)
    ) $charset;";
    
    handle_def_record($item);
    dbDelta($item['field_string']);

    the handle_def_record($item) call writes the string to the definition table. Then dbDelta actually creates the file.

    If you’re not familiar with dbDelta, it’s pretty handy. It creates the file you’ve described if it doesn’t exist. But if it does exist, it examines it and adds or deletes fields as needed based on comparing the existing file to your CREATE TABLE string.

    I wanted to add a way for users to add fields to tables (and perhaps to create new tables), so I wanted to grab an existing string from the table with the defining strings in it, parse it into the individual fields, and then present a form where new fields could be added (deciding to worry about deleting fields later).

    Toward that end, I opened the PHP file of a “table” block I’d recently created and gave Cursor this prompt:

    let's add a function called read_table_def that reads the record with the table_name field equal to 'pz_person' and returns the field_string value for that row. 

    Cursor gave me this:

    // ... existing code ...
    
    function read_table_def() {
        global $wpdb;
        $table_name = 'pz_person';
        
        $result = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT field_string FROM {$wpdb->prefix}pz_table_def WHERE table_name = %s",
                $table_name
            )
        );
        
        return $result;
    }

    Some things of interest here:

    1. Although it could confer that I was working within WordPress from the file I’d opened, I didn’t explicitly say so.
    2. It figured out and knew enough to know that it would need the $wpdb global.
    3. It wisely uses $wpdb->prepare(), something I’ll admit I’m often to lazy to bother with.

    Now, the code it supplied was correct, but I’d screwed up and told it the wrong file name, insofar as I’d forgotten the WordPress database prefix.

    Debugging and debugging the debugging

    As a result, I asked it to add a bunch of debugging code, which it did, leading me to realize, within ten minutes or so, that I’d screwed up.

    At that point, I asked it to add the prefix, and it did:

    function read_table_def($table_name) {
        global $wpdb;
        
        // Debug: Print the actual query
        $query = $wpdb->prepare(
            "SELECT field_string FROM wp_pz_table_def WHERE table_name = %s",
            $table_name
        );
        echo "Query: " . $query . "<br>";
        
        // Debug: Print the table contents
        $all_records = $wpdb->get_results("SELECT * FROM wp_pz_table_def");
        echo "All records: <pre>";
        var_dump($all_records);
        echo "</pre><br>";
        
        $result = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT field_string FROM wp_pz_table_def WHERE table_name = %s",
                $table_name
            )
        );
        
        // Debug: Print the result
        echo "Result: ";
        var_dump($result);
        echo "<br>";
        
        return $result;
    }

    Of course, I didn’t want the prefix hardcoded. An easy fix. I asked Cursor to remove the debug code, which it did. Then I moved on to a function that would take the string and parse it into an array of individual field definitions.

    I’ll skip over the details, but once again, I’m fascinated by the things it “figures out” on its own, for example, telling me that it’s making the assumption that the fields are comma delimited, but it’s an easy fix if it’s not right about that.

    It’s strangely like working with an actual mental process. It’s probably terrible news for junior programmers, if for no other reason that it pretty much never screws up things like the parameters and formats of arcane system function calls.

  • The Creator Business

    I think the creator business is probably a little confused about itself, about where the edges of what’s a creator business can be found, but that’s fine.

    What I like about the general concept is how most creators have some important web and online needs in common. Most other businesses have at least some parts of the same set of needs, but the scale and interconnection of the tools used to address those needs is actually fairly different.

    It’s hard to imagine a creator business getting much use out of Salesforce (though no doubt somebody’s about to tell me otherwise). It’s too complex, requires too much interaction on a per client or per prospect basis, and so on.

    Alright, maybe there’s even an argument to be made that Salesforce could make sense when dealing with the actual thousand true fans if that’s the way you’re thinking about what you’re doing. But even there I don’t really see it.

    Home is where your home page is

    You need a web home. You need your own mailing list of prospects and followers. You need a mailing service to get email campaigns sent out, possibly you need a drip campaign type of capability.

    You need to be able to keep the books, possibly you need to track inventory, possibly you need to generate invoices. But you don’t, most likely, need the whole kit and kaboodle of a full-blown accounting package (even one targeting small businesses like QuickBooks). You may wind up using some of the more conventional tools, but you’ll just be using the outside edge of what they can do (which includes all the things you don’t need).

    You may have paid subscriber needs, or you might want to be supported by a more Patreon-like (pay by the work product item, for instance) approach, and so on.

    Build by plugging things together

    What you need is a sensible platform where you can maintain your own web presence and, ideally, layer on the tools you need in a way that keeps things minimal and manageable by a solo operator or a small team. Almost all the things you need to do can be handled by a seeming universe of SaaS operations with annual subscriptions, but it you’re not careful you wind up paying a big stack of monthly fees for things that you wind up figuring out how to interconnect into a system on your own.

    While PeakZebra’s initial product vision wasn’t targeted specifically at creators, it’s use in the creator economy became increasingly obvious as we moved forward building our toolset. You want newsletter signups, but not lots of extra baggage managing your lists. You’d like interactions with users that let you learn more about them individually, but in a way that allows you to mass customize the content you present each one.

    You need subscriptions? We do it by harnessing one of the most-used WordPress plugin options (but you see it as part of our offering–no setting up, configuring, and learning to navigate completely new systems). You need reminders sent to members whose subscriptions will expire soon? It’s in there and it’s dead simple.

    We’ve got some things to add before this makes total sense as a use case for PeakZebra, but we’re well on the way, so if you’re a creator, you might want keep an eye on us.

  • A Modest Licensing Proposal

    Hey OSS folk: we need to start thinking outside the conventional GPL licensing box. We need a rational ecosystem for paid plugins and themes (in WordPress) and analogous capabilities on other OSS platforms. I think we can create a much better arrangement for all involved.

    I envision a licensing system that allows participation in an overarching governance system. If you want to take part in a project that uses this system and use its associated .org repositories and so on, you’d get a license for any internet-facing deployment. You’d pay a small licensing fee, let’s say five bucks a year, calibrated downward as needed to take differences in international buying power into account. Five bucks gets you a vote. For some number of sites, let’s say two dozen, a single five buck payment would get it done, but for big players, measured by sensible metrics but I’m not sure which, more cash would be involved. And bigger players would have more (but not infinitely more) votes.

    It’s a board!

    The main task for voters would be selecting a three or five-person board, but for really large issues (classic editor or blocks, to take an example from the past), direct votes might be called.

    In a greenfield new project, I’d propose a mechanism where the project creator got a big block of votes, such that they could be benevolent dictator for a while (I think there’s value in this, early on—clarity of vision and so on), but as popularity of the project grew and more people acquired licenses, the control would naturally and gradually shift over to the community as a whole.

    Who runs the thing that run the things?

    Who would run this? I imagine a few pillars of the OSS world forming some legal entity that maintained the license sales and voting procedures. It would be possible, maybe even preferable, to run this on a purpose-built blockchain, but it’s hardly a requirement, as the community would just need to trust whoever was running it.

    Now then, what about the money? All the things you might expect the money could be used for would be what the money was indeed used for, at the discretion of the elected board. And we’re talking about a substantial amount of money, the kind of money that drives early marketing campaigns, but clearly also full-on development in the form of sponsorship for core developers and, well, other things like a project’s training videos on YouTube…all the things.

    Not GPL

    To make it work, we’d need to step away from GPL licensing. That sounds severe and even ill-advised, but just because something isn’t running under a GPL or MIT license doesn’t in the least mean it can’t be open source.

    With a non-GPL license, we could have the benefits of open source, but create ways for companies writing plugins and the like genuinely to keep control over how their code was used. The code could remain open, or mostly open, but no one would be in a position to simply take code and sell it as their own (something GPL expressly allows). If the takeover of ACF as SCF didn’t feel right to you, this is how to fix the rules that make it possible.

    If we went this route, selling licenses for use of the governance services of new OSS projects, we could deal with problems such as though currently creating havoc in the WordPress community in a straight-up fashion. Under this kind of arrangement, Matt Mullenweg would have migrated out of a BDFL role years ago. We can’t undo the WordPress GPL license (if we even wanted to), but we could avoid wasting our time wringing our hands and making squeally noises. That’s right: squeally noises.

  • A Twin-Star Site Model

    I don’t love the name, but I think the creator economy is a real-enough thing. There are at least a couple million creators on the web, I read somewhere, who make a living at it.

    My impression is that they either go with something like Patreon or Substack as a way to platform themselves, or use Gumroad to sell things, or exist more or less entirely on YouTube, raking in the ad money. Maybe you can run your whole shop on Patreon (they’re introducing a community capability, founder Jack Conte even has a whole theory about how this makes sense and indeed, he does make some sense).

    Creators and cobblers

    It seems like most creators cobble both their online presence, the tools they use to manage that presence, and the back-end tools for accounting and stuff out of various pieces.

    I suspect a lot of time gets wasted in the cobbling and the learning involved with these tools. Fact of the matter is, for most creators, they typical tools are just way to feature rich and, as a result, complex.

    I’m not a creator in the sense that I’m talking about in this post, but I have done some of my own cobbling, enough to notice that, for instance, Quickbooks is just way, way, way more capability than I need, because all I need is to send out and track a few client invoices. And at nearly $40 a month for Quickbooks, I’m paying way too much for the privilege of letting them email my invoice forms.

    So I’m dropping Quickbooks in the new year and the general plan is to eat my own dog food. (I hate that expression, I think because I find life analogies that use food inherently coarse. But I can’t think of a better alternative–send help.) I’ll use PeakZebra to knock out a dead-simple invoicing system.

    PeakZebra(s)

    Whatever I pull together using PeakZebra, my plan is to evolve PeakZebra to support a “twin site” scenario where one site (PeakZebra.com) is the public facing site and another site (PeakZebra.something_else, presumably) will handle the back end things.

    This means that some elements will reside on the front-facing site, things like newsletter signup forms, to pick the simplest example. But when that signup form is submitted, it will be sent via an API call to the PeakZebra code on the backend server. The data from the form will be stored in the backend server’s SQL database, and when it comes time for me to do something with the data stored there, I’ll log in and use apps on the backend, while the front end site hums merrily along.

    Enhanced security

    With the right setup, this is a more secure approach to managing things like subscriber data, because you can put a lot of controls in place around who access that site than you can on a site that you want anyone and everyone to be able to at least see. And while WordPress is secure when properly configured, it’s safer still if the data isn’t even on the visible site.

    We’ll see how this goes–it’s not an immediate priority to have a “twinned” site arrangement, but I can still work on the sorts of simple tools I want, running them for now on PeakZebra.com but eventually migrating the backend stuff to a backend WordPress install.

    Is a twin site actually more secure? I think so, but I also think the crux of the question comes down to how secure you think the API calls that the front will make to the back will be. For my money, those can be locked down pretty darned tightly.

    You wind up with an arrangement that most attackers won’t have encountered before, with API calls being made from server to server. The attacker will not ordinarily have any way to see the API request data, nor will they be able to see the second server on the net unless they find themselves within a fairly narrow IP address range.

  • Imagining a WordPress Greenfield

    Just suppose, just for a moment, that you and I were tasked with creating something every bit as wonderful as the wonderful parts of WordPress, but starting with a (mostly) blank slate.

    It’s the plugins

    Well, one thing we have to get out of the way right away is what to do about the huge number of useful plugins that are the particular strength of the WordPress platform. Do we want to walk away from the ecommerce plugins, the learning management kits, the membership tools?

    If we want our cake and the eating of it, we’ll have to reckon with the immutable fact that all of that stuff is written in PHP and it all runs on the server. There are no lambda plugins. There are no client-side plugins.

    And it’s PHP. Some folks argue that PHP is antiquated, but I think that’s about fashion more than sense. It’s a fairly sophisticated language, performant, all that.

    All JavaScript?

    But as long as we’re declaring a fresh start, you’ve got to reckon with the hard truth that PHP doesn’t run on clients. Essentially, JavaScript is the only choice in that regard. And if you’re running JavaScript on the client, it makes life a lot easier to be running the same language up on the server.

    If we change gears and use Node.js on the server, then the challenge is finding some way to continue using existing plugins.

    I’ve turned this over and over in my mind. On the one hand, it seems pretty likely that a WordPress-specific translator could be built to turn plugins in Node.js plugins. If we do that, though, then we have to support all the action and filter hooks.

    So, on the other hand, maybe what we want is to make it easy for plugin providers to rewrite their code bases anew. Assuming an approach that was generally similar to WordPress’s approach, developers would have a pretty good sense of how they should approach various tasks.

    For example, you’d probably want get_current_user() to be getCurrentUser() and you’d probably want it to return a user ID. And you’d want to have roles and the roles would be collections of capabilities.

    Post much?

    That’s easy enough (maybe), but do we really want to commit to preserving the concept of everything being a post? We’d have to give that one some thought, but maybe the easiest way forward is to stick with posts and pages and custom posts and such.

    There’s a lot of complexity in core, though, the inevitable result of organic growth over twenty years, and maybe we just carefully sort through and discard a lot of the baggage that still works but probably was never such a great idea. And maybe we tack on a new concept or two, like having a React-like routing system be the default.

    But what we want is for WooCommerce defectors to have a straightforward sense of how to write a new and significantly more straightforward ecommerce solution. NewCommerce?

    Astro adoption?

    There’s another question to consider: given the complexity of building this sort of system, possibly the best way forward is to start with an existing system and then add on whatever stuff is needed to support critical plugin rewrites.

    This is the line of thought that has landed me at the front door of the Astro community. If you’re unfamiliar with Astro, it does content sites, like our friend WordPress. And it’s server centric, like WordPress. But it’s also vastly newer and thus not yet covered in all those rustic barnacles.

    It appears to have themes. It doesn’t, as far as I can tell, have plugins in the sense one has them in WordPress, but it seems at least conceivable that an interface to a plugin system could be created. It doesn’t have an in-built editor, but again, that seems like something that could be whipped up. Or, in a funny little twist, I feel pretty darned confident that the WordPress block editor could be pressed into service.

    So I’m going to explore Astro. Not because I’m so convinced that the current troubles in the WordPress world are going to lead to WordPress falling apart, but because I think we all need to think about hedging our bets. And, frankly, because there may be better options out there in the blog-and-content-creation universe.

    So, more to come on this. One clear takeaway, though, is that WordPress has built up an enormous and enormously useful ecosystem and feature set over the years. Leaving would be painful.

  • Headless WordPress and why it matters

    You know there’s headless WordPress, but may not be clear on how you’d make it happen. Or, more importantly, why you’d make it happen.

    What is headless WordPress?

    Let’s start with a quick rundown of what makes a WordPress site headless, why the naming in this case is exactly backwards, and just generally get ourselves on the same page.

    The conventional headful approach

    Normal WordPress is a world in which the action happens on the server. A website visitor requests a page from the server and the server assembles the page components (header, body, footer) from the database and any relevant templates. This is sent to the browser, any browser at all. And if something happens down there on the browser, it will result in a new page being requested from the server.

    Where this basic operation is perhaps most clearly visible is when the site provides some kind of data application. Maybe it’s a CRM application, so you might request a list of clients in the system. You get a display of the first 25 of them from the server, say. If you want to see the next page of clients, a new page will be requested from the server. If you want to see a particular client, a new page will be requested to display that client’s information. If you change the information for that client and want to save it, you’ll submit a form to the server and a new page will be delivered to show the update.

    Meanwhile, in the rest of the universe

    For most of the rest of the web, this isn’t typically how an application works, however. If you start with an application that shows a list of client records, then when you want to see the next page of them, a request will be sent to the server to retrieve only the data for the clients that need to be shown. The page with the client list won’t be replaced; rather, the new set of clients will be displayed on the existing page where the previous clients were listed.

    It’s possible that you can edit any of the client fields you can see on each row of the listing. Let’s say you do this and press a save icon at the end of the row you’ve changed. Again, this doesn’t result in a new page being requested. Instead, the listing continues to show the change you made and the change is sent as an update request to the server.

    The server, in other words, is just supplying data at this point, not pages (though, in our scenario, it probably supplied the initial listing page).

    Decouple this

    There are two things we should notice about this scenario. First, there’s got to be some kind of back end that answers requests for data and updates, even if it’s not supplying the pages. Second, the pages still have to come from somewhere. But the pages and the data don’t really have to come from the same place, and thus we can say that the presentation and the data have been decoupled.

    When you decouple the head of a thing, well, it becomes headless. So the baseline idea of headless WordPress is that there’s a WordPress server running, but it’s not supplying the pages that the website visitor is seeing.

    So where are the pages coming from? That depends, but most scenarios out there on the web right now fall into the basic pattern of using React (or some React framework that extends React) to create pages that can be retrieved from web servers as plain HTML and JavaScript files. These pages aren’t assembled or calculated on the server end, they are simply sent to the client as they stand. You’ll hear these scenarios referred to as static sites. That’s because the server doesn’t muck around with them–they can be plenty active once they are displayed in a browser window.

    One question that may already have popped into your mind is: what is React? And that’s an excellent question, but not one that we’re going to answer in any detail here. Suffice it to say, it’s a pre-built set of capabilities implemented in JavaScript, where the capabilities mostly have to do with user interactions.

    The key thing is that JavaScript and React are capable of asking the server (or more than one server) for data that it needs to display. The server that sends the data down to the browser in the headless WordPress scenario is, you guessed it, a WordPress server.

    Headless

    There are plenty of headless scenarios where the server isn’t a WordPress server and there are even scenarios where there arguably isn’t a server in the traditional sense.

    But we’re talking WordPress here. In that scenario, there are two primary ways that WordPress might interact with whatever’s going on down there at the browser window. It may, in the older and more widely adopted approach, use a REST API to make requests for data (or requests to place or update data on the server). Making a REST call is based on requesting a particular URL and it either places any changeable data at the end of the URL (as parameters) or it arranges them in the same way you might arrange data when posting a form to a web server.

    The other approach out there these days involves using a Graphql interface. This is more like opening a window directly into a database and making queries. The details of this don’t much matter for this discussion, the point is that it’s possible to install a plugin that creates a Graphql access point for a WordPress site.

    Wait, but why?

    Why would you take this headless approach, though?

    The obvious first answer is that it enables you to have a different language and framework running on the client side of things. If you want a React application that serves up a lot of server-side content, using WordPress as your CMS might very well make sense.

    Additionally, though, it gives you the capability to render and rerender a page in sections, so that you aren’t necessarily requesting a whole new page from the server every time anything happens.

    Now, as it happens, you can pull off this same trick using the new Interactivity API in WordPress, because it makes the front end capable of doing various things on its own. It let’s you build a “headless-seeming” user experience completely within a WordPress context.

    It’s not clear yet how well the Interactivity API will fare, as it’s still relatively early days, but it’s an interesting option for dynamic front ends (plus it’s in use within WordPress core, so it’s not likely to go anywhere anytime soon).

    WordPress makes a pretty solid CMS, particularly where the content is of the human-readable sort.


  • Creating a Block with Cursor AI

    I’ve been trying to figure out what the best approach to getting the most productivity out of AI-assisted coding in Cursor. Some things work jaw-droppingly well. Others create a rabbit hole of inexplicable coding failures that are more trouble than they are worth to debug and make work.

    Here’s a very simple example of something I was working on this morning: I wanted a WordPress block that would show an alert on a page with whatever message I put into it, and I wanted it to disappear on its own after it had been on screen for ten seconds.

    Keep it simple

    The takeaway, if you don’t care about the details, is this: you should stick to relatively discrete steps you’re asking it to achieve and you should check each bit carefully as you assimilate it into the code.

    As a placeholder, I’d been using a block from the WordPress repository called simple-alert-blocks. The simple part of the name doesn’t mislead. Nothing wrong with that, but it doesn’t fade.

    So I thought, I’ll add Cursor to make changes to it so that it always disappears after ten seconds.

    Only too happy to oblige

    Cursor happily did all this–it even looked pretty good as code, just scanning over it–and it didn’t work. Absolutely nothing happened. I started by asking it to debug the problem and it happily provided me with a “fix,” except that the fix simply re-applied code that was already there. So this improved nothing.

    I took a few minutes to look at it, but wasn’t seeing the problem. Later I realized that it had probably created a mismatch between the class name it was applying to the message box and the class name it was using in the css file. The class in the css file was .wp-block-pz-alert whereas the actual element in the DOM was using .wp-block-create-block-pzalert.

    I say this was most likely the problem because I didn’t have the code by the time I figured it out, but that CSS mismatch was a theme throughout the whole process.

    Keep your context ungoofed

    Leading to another takeaway: if Cursor does something substantially screwy, don’t just fix the problem, reset your context so that it isn’t still chewing away with the wrong idea in the back of its mind somewhere.

    So then I asked it to just create a block that did what I wanted from scratch. This one also looked pretty good, but wasn’t registering the block once I’d activated the new plugin (for once I didn’t forget the activation step). Again, strong suspicion that the mismatched CSS was in play, but there was also some idea that I’d inadvertently introduced because the original simple alert box was still in context that there should be a separate .JS file that had a “fade” function in it rather than just including this in the view.js file.

    Doublechecking

    At this point, I decided to take the AI part in far smaller steps and now created a new block using the create-block script. From there, I asked for specific steps and checked that each step worked. It occurs to me that Cursor currently makes about the same number of mistakes as I do, so I should doublecheck my progress in more or less the same way.

    This doublechecking process eats away at the time advantage of having Cursor just blast out a whole plugin, but is still way faster than my usual process. This is in part because it remembers all the function definition and syntax details that I routinely forget. It’s massively more capable than the sort of autocomplete you get with something like Github’s Copilot.

    Even in this process, it fouled up the CSS again. But it did lots of other things–using details that are very specific to WordPress block development–and had no difficulty with it.

    I asked it to add an attribute to the block, one called “messageText”:

    Note, though, that it decided to freelance on the “name” attribute. I did not tell it to create a pz domain and there were lots of other blocks in context that do it the way I wanted it, so conceivably it might have figured it out.

    Anyway, adding the attribute worked just fine, but in typical computer fashion, there were plenty of obvious related tasks that I had to specifically ask for.

    Initially, the suggested changes didn’t include importing the TextControl component, so it didn’t work and this is one of those problems that doesn’t really throw any errors, it just quietly refuses to register the new block.

    I asked it to fix this and it replied with the cheery bullshit one sometimes gets from LLMs:

    And then the line was there. But you have to wonder…

    Anyway, when I really got down to brass tacks and was explicitly asking it to make changes in chunks that I could quickly doublecheck, the process went quickly. It would have gone even quicker if I’d reset the chat and started with fresh context.

    More reports from the coding trenches to come…

  • World’s Simplest CRM

    Here’s a use case for PeakZebra that I think makes all the sense in the world.

    We begin with the thought that most of us actually don’t need lead scoring automation.

    Just the facts

    You do need to be able to store information about your prospects. And if they they become customers, you need to make a note of this, either within the same database or by transferring them to some other system. And you need to track their payments and so on.

    For a lot of companies, there’s not actually all that much distance between prospect and customer. For instance, someone contacts you to ask for details on the professional service you offer. You talk with them, gather some info. Maybe you make them a written proposal. They accept the proposal or they don’t, but even if they don’t they aren’t, should some future opportunity lead to a new proposal, exactly a run-of-the-mill prospect anymore.

    Whether a potential customer or an actual customer, you need their contact information and you need to keep a record of your interactions with them. If they send you an email with a pre-sales question, you want to keep a copy. If you give them a Zoom tour of the product, you want to have a record of when and what they thought about it.

    You need to be able to track any deliverables you’ve promised and you need to be able to set reminders for future follow up.

    If you’re really on your game, you can also be learning about them as they interact with your web site and you can build up a segmentation model for your customers (and your prospects, which is arguably almost more important). That work happens on your customer-facing websites, of course, but PeakZebra supplies some great tools for that.

    Not Rocket Science

    The key point here, though, is that none of this is actually complicated. Salesforce will do the job, but you’re paying for the whole lot more that it can also do. Like lead scoring. Even at the small business entry level version, this is $25 per user per month. The next step up is $100 per user per month.

    The PeakZebra approach is dead simple. There’s a “Person” grid that shows contacts. If you want, you can easily have different grids for prospects and clients. If you click “Add a New Person”, you get a form for that.

    Out of the box, you can associate each contact with a particular internal team member. You can tag each person with whatever tags you like (you create them), and you can make a note of each interaction you have with them over time.

    Simple, then customized

    And that’s it. Unless you need something else. In some cases, you can simply add on things you want, if that’s your preference. But you can also queue a request for the change. We generally deliver them within two business days and at a very low cost compared to hiring a conventional developer to jump in cold.

    You can learn more about the pricing model for this sort of change by signing up for our occasional email updates.

  • Naming the Business Model

    First and foremost, PeakZebra is a SaaS offering. It’s the familiar online model, where you pay monthly, except maybe I’ll do what it seems like everyone else is doing these days and force you to pay for a year (which, even if I wind up doing it, I hate).

    But there’s a second layer, where you can, if you choose, pay for individual tweaks to the applications you’re using (well, tweaks right up, theoretically at least, to wholesale creation of full-blown new apps). The model here is what’s been called “productized services,” which is an odd name but one that does manage to convey a sense of the value proposition, which is that you pay a controlled and fixed amount for your use of what otherwise might have been charged on a project or hourly basis.

    Agency?

    The “thing” you get from PeakZebra is a customizable website, so another way of thinking about this is to call it an agency. But it’s really not an agency in the traditional sense where you show up and request a customer-facing site that represents and explains your business to the internet.

    Why do I care what to call the business model? I only care because I want a quick way to communicate what’s on offer. “Internal tool agency” doesn’t do it for a few reasons, I’d say.

    Oh, and it seems obligatory that I convey the idea that AI is involved, because that seems to be the only thing that anyone is paying attention to in new products and services these days. I noticed just now that Airtable’s H1 is now “Digital Operations for the AI Era.” To be honest, this makes me instantly just the tiniest bit suspicious. And to be fair, it’s not like I even know what it is that they’re using AI for.

    AI Magic Broker?

    The irony is that I do absolutely plan to be using AI behind the scenes (as per several blogposts by now), but I’m actually more inclined to be relatively quiet about it. It’ll make things fasters for developers handling requests, but the key point is that there’s an actual developer involved.

    So maybe I focus on it being a SaaS for ad-hoc apps, but a subscription that makes it seamless and easy to mold the app to your needs. If you have a real management job already, you might very well be not even the least bit interested in spending time learning how to do this and that in Airtable or Notion (or pick some other preferred magic no-code SaaS).

    Maybe “guided low-code automation at a fixed subscription price.” OK, that’s too long, so maybe “subscription app refinement.”

    Low-code Whatever

    Actually, I hate that one. I still have some noodling to do on this, I guess. I think getting it right will give me greater clarity when I try to tell people what the “thing” is that should grab their attention about PeakZebra. Sure, it’s a way to pay for the world’s least-featured CRM, but there’s a little more to it than that.

  • Is the WordPress Plugin Economy Played Out?

    People have made some very impressive business successes by offering non-free plugins and plugins that are extensions to other plugins that are big enough to have extensions of their own (WooCommerce being the primary example). But I think it’s increasingly unlikely that this is a good way to launch a successful business.

    [Since originally writing this, it’s also occurred to me that the agency approach to making a living in the WordPress space may also be living on borrowed time. That’s because high-end website owners and their agencies may opt to avoid the perceived risk of WordPress coming unglued and may in any case prefer different architectures while, at the same time, lower-end sites (blogs for authors and the like) are increasingly very well served by “drag and drop” sorts of offerings. I’m thinking of website builder SaaS offerings, certainly, but also platforms like Substack and Patreon. If my goal was to create a subscription newsletter, I wouldn’t presently choose a WordPress site if I wasn’t already very familiar with WordPress.]

    I’m thinking primarily of bootstrapped businesses, but I also suspect that bootstrapped startups may be all there is in the WordPress space, at least for a while, because Matt Mullenweg’s takeover of Advanced Custom Fields (and his subsequent takeover of parts of the premium version of ACF) has, I’d imagine, significantly cooled the enthusiasm of venture capital investors for premium plugins.

    In the bootstrap world, there’s probably less reason to fear that some Matt out there will steal your goodies, but that doesn’t mean someone else couldn’t. The ACF heist was a poignant reminder that you really don’t own your own code in the world of open source (which, come on, was the whole point of open source in the first place, so don’t act shocked).

    As I write this, there’s more of a serious movement afoot to revise WordPress governance so that it’s more of a community driven project (instead of a one-guy project that has a huge community). But who knows how that will play out.

    SaaS as a solution

    As I’ve mentioned elsewhere, one approach that protects whatever secret sauce you bring to the meal is keeping the sauce on the server side of a SaaS offering. For a lot of plugin builders, the trick will be simply not to package the plugin as a plugin.

    But I think there’s another issue, and I even think it’s one of the inherent strengths of open source as a concept: over time, free versions of things get better and better. You may have a feature-laden free version and a whole bunch of great goodies to hold in reserve for the premium version, but over time you’ve going to see other developers make competing products that offer some of your premium features for free.

    Freemium limps along

    I don’t mean to say this kills the freemium model, only that the tendency is toward making the premium side of the equation less valuable over time. Given that pricing in the WordPress ecosystem is arguably too low, this seems likely to make would-be entrepreneurs not exactly thrilled to jump in with both feet.

    This is too bad, because there’s nothing quite like the WordPress plugin ecosystem (or the theme system, for that matter) out there in the software world, and it’s offered a way for a lot of smaller businesses to get a foothold and make a go of it. Not to mention creating ways to create all sorts of non-business blogs and the like.

    But I think this shrinking premium benefit phenomenon is inherent in current-day WordPress and open source. There’s less and less incentive to spend a year rolling out a paid plugin. If you win, you don’t necessarily win much, your potential winnings aren’t secure, and competition from free alternatives is going to slowly erode your niche.

    Will the wolf survive?

    So do people like me stick with WordPress? Well, if we’re specifically looking at my case, then yes. I stay in WordPress, trying to leverage opportunities that take advantage of the platform’s enormous market share without getting bitten by the current growing risks to WordPress business.

    (I will say I’m tempted to just switch up and go full time on whatever Joost and company wind up concocting–but that too will be WordPress in some form or fashion.)

    I’ve got to believe that when a framework on the Internet has 40% market share, there’s still lots of opportunity to supply things people need, even if it’s a smooth, easily understood offramp from WordPress.

    All that said, though, I’m still inclined answer the question I started with–is the plugin business played out?–by conceding that, yes, it may well be.