Scott’s Vegetable Fried Rice

Even before COVID-19 hit, back when we were getting takeout at least once or twice a week, this was a staple meal I’d cook… uh… almost as often as we were getting takeout. Now I make it two or three times a week, because it’s reliable, satisfying, and I have it down cold.

Any time I realize I have a recipe down cold, and that my own technique for it has probably deviated somewhat from whatever recipe I originally followed when I needed to follow a recipe, it feels like I should write down my own recipe. For posterity, or whatever. Anyway, here it is!

I’ve adapted this recipe slightly for the time we’re living in. Normally we always buy fresh produce, but as stay-at-home orders set in, and I wasn’t sure what the future held, grocery-shopping-wise, I bought some bags of frozen vegetables. Frozen vegetables don’t take as long to cook as fresh, so that affects the timing and sequence of adding vegetables to the stir fry.

Scott’s Vegetable Fried Rice

Makes… a lot. But you’ll eat a lot of it, so it’s pointless to try to say how many servings it is!


All quantities are approximate. Use as much or as little as you want, and feel free to omit or substitute vegetables.

4 c or more cooked jasmine rice
1 package extra firm tofu
1 medium onion, rough chopped
1 c chopped or sliced carrots
1 c cauliflower florets, fresh or frozen
1 c broccoli florets, fresh or frozen
1 c chopped Chinese, Savoy or green cabbage
1 c sliced button mushrooms
1 can baby corn, drained and rinsed (optional)
1 c fresh pea pods or frozen peas
1-2 cloves minced garlic
2 eggs, beaten
2-3 diced scallions
peanut oil
sesame oil
soy sauce or soy paste
rice wine vinegar
Sriracha sauce (optional)


  1. Cook the rice in a rice cooker or otherwise according to package instructions. (Any kind of long-grain white or brown rice will work in this, but we prefer the taste and texture of white jasmine rice.) You can also use leftover rice! Note this is 4 cups cooked. It only takes about 2 cups of dry rice to make 4 cups cooked.

  2. Drain the tofu and press it to remove excess moisture. (We wrap ours in a kitchen towel, between two plates, with a heavy can placed on top, for about 10-15 minutes.) Cut the tofu into 48 cubes. (That’s 2 x 4 x 6.)

  3. In a large bowl, mix about 2 tbsp each of the soy sauce, sesame oil, rice wine vinegar and Sriracha until well blended. Add in the tofu cubes and toss very gently (they’ll break apart otherwise), until thoroughly coated. You can do this up to a few hours in advance so they’ll marinate, but I never plan ahead enough. Not a vegetarian or just hate tofu? Skip it! Or, chicken or shrimp will also work very well in this recipe. (Marinate the meat in the same mixture and stir fry it separately before the vegetables, then set aside until the end.)

  4. Spread the marinated tofu cubes on a baking sheet lined with parchment paper, and bake at 400ºF for about 10-15 minutes. The timing isn’t super-critical; you just want them to get a bit crusty on the outside. You can also deep-fry them but I prefer baking.

  5. While the tofu is in the oven you can do the rest of these steps. We’ll start with a typical stir-fry of the vegetables. This means adding in each type of vegetable every couple of minutes, so you need to approach them in order of how long they take to cook. In a large skillet or wok over medium-high heat, add 2-3 tbsp of peanut oil and a generous splash of sesame oil. When the oil is hot, add the onions and carrots. If you’re using fresh cauliflower, add it now as well. Stir fry for a couple of minutes, until the vegetables start to soften and change color. If the onion starts to turn brown, turn the heat down a bit or add a splash of water.

  6. Add the broccoli, if it’s fresh, and stir fry for a couple of minutes, again until it starts to soften and change color. If your broccoli is frozen, go right to the next step. If you’re using fresh pea pods, add them now as well.

  7. Add the cabbage and any frozen vegetables except peas. You know the drill… a couple of minutes, etc.

  8. Add the mushrooms and baby corn, if using. Stir fry until the mushrooms have released their moisture and are starting to darken.

  9. Add the frozen peas and the minced garlic, and stir fry for no more than 2 minutes. Add a splash of rice wine vinegar and stir well, then add about 2 tbsp of soy sauce or paste, and Sriracha to taste. Stir well and then remove all contents of the skillet to a large bowl. (I use the same bowl I tossed the tofu in, so the vegetables absorb whatever is left of the marinade.)

  10. Wipe the skillet with a paper towel if there’s a lot of residue, or just leave it as-is. Return to the heat and pour in about 4 tbsp of peanut oil and another splash of sesame oil. This should heat up very quickly.

  11. Add your rice to the skillet, breaking up any chunks, and spread it around in an even layer. Cook for a few minutes, turning occasionally, so that some grains get a bit crisp and brown, but not burnt.

  12. Push the rice to the sides of the skillet in a ring, so there’s a large opening in the center. Add a touch more peanut oil, then pour in the beaten eggs. Let sit for a few seconds and then stir and break up as the eggs cook. (Pretend you’re making scrambled eggs for breakfast.) Cook the eggs until they’re no longer runny, but don’t overcook — we’re not done yet, and they will cook more.

  13. Stir the cooked eggs and rice together until the egg is thoroughly mixed through. Reduce the heat to low. Add a generous amount of soy sauce or paste — at least 2-3 tbsp, and stir until well blended.

  14. Add your vegetables to the skillet with the rice and egg and stir to blend together.

  15. Take the tofu out of the oven if you haven’t already, and add it to the skillet. Stir again to blend.

  16. Remove the skillet from the heat, and stir in the diced scallions.

Optional “Dipping” Sauce

We always make this fried rice with frozen vegetable potstickers. I make a sauce for dipping the potstickers using equal parts soy sauce, soy paste, rice wine vinegar, sesame oil and Sriracha. It’s good to make a bunch of this, because it is also great drizzled over the fried rice in your bowl!

Wait… Soy Paste?

I had never heard of soy paste before, but I got turned on to it a few years ago. It’s brewed in a similar way to the soy sauce we know well in America, but it’s thicker — almost the consistency of Hershey’s chocolate syrup — and it has a deeper and less salty flavor. You can pretty much use it interchangeably with soy sauce, but bear in mind that because it’s thicker, you might need to add a little bit of water to your skillet with it so it doesn’t just burn to the bottom. When you’re pouring it on top while serving though, keep it thick!

My favorite kind is Kimlan, which I pick up at United Noodles.

Web developers: learn how to Google. If no one else has the same problem, the problem is you.

OK, maybe not you per se. This is not a judgment of your merits as a developer, or as a human being. But it does mean the problem is almost certainly something specific to the code you’ve written.

The Hierarchy of Coding Errors

If your code isn’t working, the source of the problem is one of the following, in order from most likely to least likely:

  1. New procedural code you’ve just written
  2. New object-oriented code you’ve just written*
  3. Custom functions or objects you built, but have used before
  4. Third-party/open source add-ons to the software platform you’re using (e.g. WordPress plugins)
  5. Standard functions or objects in the software platform (e.g. WordPress core)
  6. Public code libraries that are included in your chosen software platform (e.g. jQuery)
  7. Browser bugs
  8. OS bugs
  9. Internet protocol bugs
  10. Quantum fluctuations in the fabric of spacetime
  11. Gremlins

You may have guessed correctly at this point that this blog post is not just idle Friday afternoon musings. I’ve spent the majority of the day today troubleshooting a very strange issue with a website I’m currently building. I fixed the problem, but not after being forced to — once again — confront this humbling reality. If something’s not working, it’s probably your own fault. Especially if you’re the only person with the problem.

Googling the issue got me (almost) nowhere… which was the most obvious clue that it was my own fault

Aside from the natural human inclination to deflect blame, the tools we have for troubleshooting these types of problems are not necessarily well suited to forcing us to be honest with ourselves. It’s too easy to blame external forces.

Here’s my scenario. I found out last week while presenting work-in-progress to a client at their office that there was a JavaScript-related problem with the website. It only affected Internet Explorer (and Edge), which I had not yet tested the site in, and, weirdest of all, it didn’t always happen. I’d say maybe 10-20% of the time, the page loaded normally. But the rest of the time, it got an error.

Since this was only affecting one browser, my natural inclination was to start all the way at number 7 on the list, blaming Internet Explorer. But I’ve learned that as much as I want to blame it, issues with IE usually just shine a light on something in my own code that other browsers are more forgiving about. So it was time to walk backwards down the list. (Again… not really, but this is how it played out.)

The error that the browser reported was a “security problem” with jQuery Migrate. First I had to figure out what the hell jQuery Migrate was and why it was being loaded. (Turns out, it’s a place the jQuery team dumped deprecated code it pulled from version 1.9. It’s loaded by default by WordPress.)

With that in mind, this should be affecting every site I’ve built recently, since they’re all in WordPress. But it was only affecting this one site. So I had to try to narrow down where the problem exists. With WordPress, there are two main “variables” in the implementation: themes and plugins. When in doubt, try switching your theme and disabling the plugins you’re using. I started by disabling all of the plugins, one by one. No change. I found the error didn’t occur if I disabled Advanced Custom Fields, but that’s because half of the page didn’t load without it! (That’s another error on my part but let’s ignore that for now, shall we?)

OK, so it’s not a plugin. Next I swapped in the standard Twenty Sixteen theme in place of my custom theme. Not surprisingly, the error didn’t occur, but that didn’t help much because none of my Advanced Custom Fields content was in the pages. I still couldn’t rule out ACF as the culprit. But I tend to reuse field groups from site to site, so once again, if this were attributable to an ACF issue — even something specific to my field groups — it would’ve cropped up on another site.

So now I had little left to do but selectively comment out elements of the theme so I could narrow down where the problem was. (I make this all sound like a logical progression; in fact my debugging process is a lot more chaotic than this description — I actually did this commenting-out process haphazardly and repetitively throughout the afternoon.)

Eventually I pinpointed the troublesome block of code. Yes, it was #1 from the list. But as is usually the case with hard-to-diagnose problems, the complete picture here is that #1 included a combination of #3 and #5, which triggered an error message generated by #6, but only in the context of #7.

Yes. That’s what happened.

In the footer of the page, I had a link to the client’s email address. As is my standard (but by now probably outmoded) practice, I have a custom-built function I wrote years ago to obfuscate the email address by randomly converting most (but not all) of the characters in the string into HTML ampersand entities. My problem was not that function itself, which is tried and true. It’s that in this particular instance I called it on a string that included the mailto: pseudo-protocol, not just the email address itself.

I think the colon in mailto: is particularly significant to the problem, as evidenced by the fact that around 10-20% of the time the problem didn’t occur, and the page loaded normally. Since my obfuscation function randomly leaves characters in the string alone, that’s about how often the colon would have been kept untouched.

But even then, what difference should it make? Browsers decode those entity strings and can handle them in the href attribute of links just fine. However in this particular case I didn’t just use my obfuscation function. Without giving it much thought, in this particular site I had decided to wrap the obfuscated string in the standard WordPress esc_url() function. Trying to properly sanitize things, like a good developer. Right? Except — and I took a quick look at the source code to confirm it — there’s special handling in esc_url() for strings that don’t contain a colon. So the roughly 86% of the time that my string didn’t contain a colon, esc_url() was prepending http:// onto the string.

This situation was causing a particular piece of code in jQuery Migrate to barf… but only in Internet Explorer and Edge, for reasons I still don’t understand, but it has to do with how the different browsers handle security warnings in JavaScript. I found along the way (but before I had pinpointed the real problem) that if I commented out a particular segment of code in jQuery Migrate pertaining to the handling of selectors containing hashtags (see, the HTML ampersand entities again) I could get the page to load normally.

So, like I said, my newly written procedural code (#1), which itself included calls to both an existing custom function I wrote (#3) and a function baked into the WordPress core (#5), caused jQuery Migrate to issue an error (#6) but it was one that only a particular browser (Internet Explorer/Edge) cared to acknowledge (#7).

No wonder it took all afternoon to figure it out.

* The only reason I break out OO from procedural code is that OO has more structured patterns that are less likely to result in sloppy mistakes. Slightly.