Bryan Friedman

The Evolving Technologist: Adventures of a Recovering Software Generalist

So... What Exactly Is Technical Marketing?

I've found there to be plenty of variance in the industry around job titles, so I usually don't put a ton of weight on them. I've had titles that weren't very descriptive of my actual role. I've had titles that seem to imply something that isn't even true about what I do. I've seen junior-sounding titles for people who seemed pretty senior, and senior-sounding titles for people who acted more junior.

Regardless of the names and levels, I've worked in technology for long enough to have collected several job titles that are difficult to explain at dinner parties or to family members. That's why, when I transitioned from enterprise IT into product management ten years ago, I wrote a post to help answer that dreaded question: “So... what do you do?”

Fast-forward a decade, a few roles, and a handful of technology fads later, and I’ve once again found myself in a job that even people inside tech sometimes struggle to define: technical marketing.

Despite being an important function, particularly for developer-facing products, the role of technical marketing can sometimes be confused with engineering, product, or traditional marketing. That's actually fair, though, because tech marketing does borrow elements from all three.

So how is technical marketing different?

# How Technical Marketing Fits In

Admittedly, the first time I joined a marketing team, I was a bit trepidatious about it, at least initially. Isn't marketing too far removed from the technology itself? Would I ever get to talk to an engineer or even write code again?

I quickly learned that actually, technical marketing isn't so far from product management, or any of the roles I've had in my career really. When I first got a job as a product manager, I described it as a role "in the middle", like the connective tissue between customers, engineering, and the business. Technical marketing lives in that same neighborhood, just a few doors down. While product management decides what to build and why, technical marketing focuses more on why what we built matters, and more importantly, how to show it.

I guess that might sort of sound like marketing more generally. Traditional marketing, or more specifically, product marketing, is indeed (at least partially) about telling the story of why something matters — the value proposition, perhaps you've heard it called. But it's that last piece I mentioned before, the "how to show it" part, that I think is the key distinction. A technical marketer can't just say something, they have to prove how it works and build understanding. If marketing inspires people to want to learn more, technical marketing helps them actually get there.

It's sort of analogous to the sales associate and sales engineer. When a salesperson pitches something in a room full of executives, talking about what it does might be enough. But invite some architects or developers into the room, and you better have a sales engineer there to field the tougher technical questions and show them how it works.

# What About Developer Relations?

Another area I’ve worked in and around is Developer Relations, which I’d describe as at least adjacent to technical marketing. Both disciplines are about building trust with a technical audience, so there’s definitely overlap.

In my experience, Developer Relations is primarily about cultivating a community of practitioners, sparking curiosity, earning credibility, and helping people succeed whether or not they ever become customers. It’s about awareness, trust, and engagement. Technical Marketing, on the other hand, focuses more on enablement and adoption, showing how the product delivers value, differentiates, and solves real problems for customers and partners. It's not a perfect delineation (like I said, there's overlap), but I guess you could say DevRel makes fans and Technical Marketing builds believers.

Truthfully, all of these areas — PM, DevRel, and Tech Marketing — sit along the same bridge between technology, communication, and empathy. But each one might put a little more emphasis on a different area: Product Management on strategy, Developer Relations on community, Technical Marketing on proof and enablement. I’ve been fortunate to work in all three, and each helped sharpen different skills from strategic clarity, to technical depth, and creative communication.

It's why I love these types of roles so much. They let me bring all sides of myself to work: the analytical and the imaginative, the engineer and the storyteller, the tech enthusiast and the theatre kid. It’s where my left brain and right brain finally get equal billing.

# So...What Do You Do?

There are probably several different views on what technical marketing is and how to define it. For me, when I explain my role, I find it’s helpful to break things into two categories (the same ones I used a decade ago when I wrote about product management): what we need to know, and what we actually do.

# What Technical Marketers Need to Know

Unsurprisingly the three key knowledge areas are pretty much the same as I listed for PMs.

To do this job well, we have to know the product as more than just a list of features. We learn it by using it. We dig under the hood to understand how things work, explore the user workflows, and every now and then work through a rough spot to figure out what’s really going on.

We try to stay close to the market too. That means understanding not just who competes with us, but what existing customers and potential users are actually struggling with, what’s changing in their world, and where things are headed next. Context matters as much as capability.

The best technical marketers also pay attention to when something isn’t clicking for customers (and prospects). Whether it shows up during a demo, in a training session, or in the questions we hear out in the market, those moments usually reveal a gap in how we explain the product. That insight shapes what we build next, from clearer docs to new demos and enablement. Which leads nicely into...

# What Technical Marketers Do

To me, the most fun (and challenging) part of technical marketing is crafting a narrative. I'm not talking about inventing spin, though, because credibility is key with a technical audience. I mean we distill the heart of the value and figure out the most compelling way to reveal it. We make technical concepts feel relevant and even exciting.

In my day-to-day, that might look like:

  • presenting a live or recorded demo
  • recording feature walkthroughs
  • building, facilitating, and maybe even delivering training courses
  • writing technical content
  • developing competitive materials to help sales and partners position the product
  • enabling field teams with deeper technical context
  • giving live product demos at tradeshows or events (I was doing this in only my second week on the job!)
  • taking questions, confusion, or objections and turning them into clearer messaging or new content

It's a lot of learning in public, which can sometimes mean pushing through impostor syndrome to ultimately show expertise and prove the narrative.

If you’re curious what this all looks like in practice, here are a few recent examples I had a hand in:

# Why I Love It

Across every role in my career — IT, product, developer relations, and now technical marketing — the theme has been consistent: translate technology into possibility and turn complexity into confidence.

What’s great is that I get to blend analytical precision with creative expression. The architecture diagrams matter, but so does the storytelling arc. The tech and the theatre kid get to show up every day. That combination is where I feel most at home.


Life Finds a Way with OpenRewrite Part 2: Code Evolution

When I last left off, I’d done the unthinkable. I resurrected my college senior project from 2003—the Help Desk Scheduler. It was running again as a Java 8 web app on Tomcat 4 with Struts 1.0 and MySQL. To continue my Jurassic Park metaphor, it was the software equivalent of a creature that shouldn’t exist anymore, but somehow came back to life.

And also because, now that I’m at Moderne, I spend my days thinking about automated code transformation with OpenRewrite. (I'm super fun at parties.) So of course I wanted to see if this ancient app could evolve enough to survive in 2025. I don't need to go full Jurassic World reboot yet, but what if we can tweak things just enough to get to The Lost World at least?

# From Batch Files to Build Tools

In college, my “build system” was literally a batch file that ran javac and copied the results into Tomcat's webapp folder. To modernize anything, I first needed a real foundation. It was time to pick: Gradle or Maven?

I chose Gradle, mainly because I was less familiar with it and wanted to learn, but also because it seems to be a common choice among devs I respect. So, to get things working, I had to:

  • Restructure the directories to use src/main/java and src/main/webapp
  • Set up a build.gradle.kts file (I went with Kotlin over Groovy because I'm a follower)
  • Declare dependencies for the old Servlet API and Struts 1.0 (which required a local JAR since the old version didn't seem to exist on any repository anywhere)
  • Specify Java 8 and configure the war plugin
  • Update my Docker Compose setup to build and deploy a WAR instead
  • Add the OpenRewrite Gradle plugin since there would be recipes in my future

For the first time in two decades, HDS had an actual build pipeline. Now OpenRewrite could start working its magic.

# Automation: Nature’s Next Step

With Gradle in place, I was ready to run some recipes. I wanted to take this from barely runnable on Java 8 to something that could at least sort of live in the modern Java world. I started with UpgradeToJava21, which handled compiler targets and cleaned up a few deprecated APIs.

Next came JakartaEE11, which migrated javax.*packages to jakarta.*. What could possibly go wrong at this point?

Everything. The changes were clean, but it turns out that Struts 1.0 simply wasn’t built for a Jakarta world, and the build logs made that abundantly clear. Huh. What to do?

# Finding a Path Forward

First, I'd need a newer version of Tomcat. I got that up and running manually and figured I could automate it later. (Which I did...see below.)

Then, I considered trying an upgrade to Struts 2, but that honestly looked almost as hard as a full-scale rewrite. Same for moving off of Tomcat altogether to a Spring application. I hope to get there eventually, but this first step was just about some incremental change. I wanted to run Java 21 without too much manual effort, if possible. Could I automate everything with OpenRewrite and make it all work?

Rather than give up, I went hunting for a compatible solution and I stumbled upon Struts1 Reloaded, a modernized fork that aims "to bring Struts 1 to a current technology." This looked like the best route, at least for now. The latest version (1.5.0-RC2) supports more recent Jakarta namespaces. Sweet!

Using OpenRewrite dependency recipes, I swapped out the old framework for the new libraries. That meant replacing the old Servlet API with the new ones, retiring the old com.sun.mail packages in favor of new ones from Eclipse, and replacing the local Struts JAR with all new references to the Struts1 Reloaded libraries.

Still got a bunch of build errors, but far fewer. Getting closer.

# A Little Genetic Engineering

The errors were mostly type and method name changes from moving to Struts 1.5. Thankfully, those trusty old OpenRewrite standards ChangeType and ChangeMethodName came to the rescue for that. Action perform() is now Action execute()? No problem. Oh, ActionError is gone in favor of ActionMessage? Easy. But ActionMessages empty() needs to be ActionMessages isEmpty()? Done. Thanks OpenRewrite!

type: specs.openrewrite.org/v1beta/recipe
name: com.bryanfriedman.hds.MigrateStruts
displayName: Struts 1.1 to 1.5 API adjustments
description: ActionError→ActionMessage, perform→execute, messages.empty()→isEmpty
recipeList:
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.apache.struts.action.Action perform(..)
      newMethodName: execute
      matchOverrides: true
  - org.openrewrite.java.ChangeType:
      oldFullyQualifiedTypeName: org.apache.struts.action.ActionError
      newFullyQualifiedTypeName: org.apache.struts.action.ActionMessage
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.apache.struts.action.ActionMessages empty()
      newMethodName: isEmpty
      matchOverrides: true

But now, things got a little more complicated. There were two changes that I needed to make and I couldn't find any existing recipes to do the trick. But hey, I said I wanted to learn how to write some custom recipes. This was my chance. So I wrote two imperative recipes to handle these cases:

  1. DataSource access. The old Struts ActionServlet findDataSource() helper no longer worked. They needed to be converted to use standard JNDI lookups.
  2. Method signature. The new Action execute() method in Struts 1.5 added a throws Exception declaration, meaning any overriding methods needed to also.

I had a little bit of help from Claude Code to write these recipes. (I'd had some experience doing that at work.) But still, writing these custom solutions gave me such an appreciation for how elegant and extendable OpenRewrite really is when you need that level of precision. And the test framework is so easy to use, you can see exactly what needed changing in both cases.

One more little ChangeType tweak to broaden some exception handling, and the build finally worked. Too bad the run didn't...

# JSPs, XML, and Other Endangered Species

Now the app was failing to render, so I knew it was time to look to the JSPs. In fact, the Struts 1 Template tags had been retired in favor of Struts Tiles. That meant a whole host of changes to the JSP files would be required.

Although OpenRewrite doesn’t parse JSPs, I was still able to automate into my recipe by using the text-based FindAndReplace recipe to do some regex magic.

And finally, for the XML config files (web.xml, struts-config.xml, and Tomcat 11's server.xml), I used some XML recipes to make the necessary changes, and some Create*File recipes to drop in the new ones.

# It's Alive... Again

After all the dust settled, the project now:

  • Builds cleanly with Gradle
  • Includes a build step within Docker Compose
  • Runs on Java 21 / Tomcat 11
  • Uses Struts 1.5 (Reloaded)

Is it modern? Not really. But it does compile cleanly, deploy reproducibly, and doesn’t require completely ancient toolchains to run. I'm quite proud that (after manually setting up Gradle) I completely automated all of the changes using only OpenRewrite. It was all refactored through deterministic, repeatable automation. No frog DNA required.

# Evolution, Meet Ambition (and Chaos Theory)

There’s a point in every old-code resurrection where you remember Dr. Ian Malcolm again. Just because you can modernize something, doesn’t mean you should.

That said, though, I’m probably not done experimenting. Some next steps I’m considering:

  • Swap MySQL for Postgres. (Probably only config changes?)
  • Skip right over Struts 2 and try a Spring Boot migration, just to see how far automation can carry it.
  • Move off of JSPs to...something else?

Each attempt is both an exercise and a curiosity test: how much of this can be done through recipes rather than rewriting by hand?

Part 1 was the resurrection; this sequel is the evolution. Give it a few more transformations and I'll have my own cinematic universe.


Life Finds a Way with OpenRewrite: Resurrecting a Long-Extinct Java App

I'm sure you know this classic line from Jurassic Park:

“Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.” —Dr. Ian Malcolm, Jurassic Park

That quote was about cloning dinosaurs, but honestly, it also applies pretty well to a scenario I'm now facing: resurrecting long-dead source code.

Recently, I joined the team at Moderne, where I’ve been diving into the world of automated code remediation and transformation using OpenRewrite. It’s powerful tech that can scan large codebases and apply structured, deterministic changes like it actually understands the code (because it basically does).

I've been wanting to write some custom OpenRewrite recipes as a learning exercise, and I've been looking for a good idea to try out. This got me thinking: What kind of code would be fun (and maybe a little terrifying) to experiment on?

That’s when I remembered Help Desk Scheduler, the scheduling system I built for my senior project in college circa 2003. It was powered by Struts 1, backed by MySQL 4, and built with Java 1.2 with no build tool in sight.

What could possibly go wrong?

# Digging Up Fossils

The Help Desk Scheduler (HDS) was a handy tool I created while working at the Cal Poly ITS Help Desk. It was a web app that generated work schedules for staff and students based on user-defined rules. It let supervisors schedule student workers, manage shift swaps, and view schedules in various formats. It solved a real problem, and it actually worked!

Back then, frameworks like Struts 1 were still new and exciting, Tomcat 4 was the default server choice, and MySQL was all the rage. For this project, I built everything with raw javac commands in a batch file. No WAR file. No CI/CD. Just a folder full of class files and a dream.

Somehow, I had kept a copy of the source code, preserved like a mosquito in amber, complete with J2EE DNA. So I decided to see: could I bring this fossilized application back to life and use it as a playground for OpenRewrite?

# Reanimating the App

Getting it running again meant digging through ancient APIs, outdated assumptions, and a build process so flaky that I can hear Dennis Nedry now: “Uh uh uh, you didn’t say the magic word!”

Here’s what it took to get it running in 2025:

  • Sticking with Tomcat 4 (v4.1.24 downloaded from the Apache archives)
  • Using the oldest "supported" version of MySQL: 8.0.13 (plus a newer JDBC connector)
  • Tweaking the database schema and JDBCRealm configuration to support MySQL 8
  • Swapping out JavaMail SMTP for MailDev (good enough for these purposes)
  • Dockerizing everything with docker-compose to make things easy to run (and portable)

After a weekend of tinkering and a few "I can't believe this is working" moments, I had the app running again in my browser, in all of its ugly, table-based, CSS-less glory.

Check out the Help Desk Scheduler GitHub repository.

# But... Why?

Let’s be clear: I have no plans to offer a commercial or even production-ready app. But having a working legacy app gives me a useful, safe, and fun sandbox for experimenting with modern code transformation using OpenRewrite.

Some experiments I’m thinking about trying on this codebase:

  • Migrate from MySQL to PostgreSQL
  • Upgrade to Java 21
  • Replace Struts with Spring MVC (or Struts 2, or something else)
  • Move hardcoded config to external properties
  • Swap out the authentication layer
  • Eventually maybe even refactor into something that resembles a modern Spring Boot app

Most of these are squarely in OpenRewrite’s wheelhouse, but I can also try for some stretch goals to give me a way to explore how far automation can take things vs. where manual intervention may still play a critical role.

# What’s Next

Now that I’ve got this prehistoric app running again, I plan to document my OpenRewrite experiments in future posts. I’ll explore what works and what breaks, and where automation helps or not when dealing with very old Java code.

But for now, I’m just happy that I got this dinosaur of a project running again. Not because I should, as a wise chaotician might say, but because I could. I hope the servlets don't bite!