▲ 7 r/daddit

Tonight was the truth about the tooth fairy (etc)... "There's no magic anymore" 😢

I don't know why, but I didn't really plan for this, and it took me off guard emotionally. My 8 year old interrogated me at bedtime about whether the tooth fairy was real, or whether it was just us - saying that she heard it was just parents. She said she could handle it and just wanted to know the truth. I told her that yes, mom and I pretend to be the tooth fairy, because it makes thing fun.

Well, within 5 minutes, she figured out all the rest. No leprechauns (this was a surprisingly dear one to her), no Easter Bunny, and no Santa. What followed was about half an hour straight of sobbing. The things she said just hit me harder than I thought they would. Stuff like...

"There's no magic anymore, daddy. That's why I'm sad."

"Why didn't you tell me when I was younger? I would have cried even more, but now that I'm older, my tears are different."

"I wish I didn't ask you."

"Why do parents lie to their kids?"

"Now there's nothing special in the world."

As she finally calmed down, and as we continued to talk, she did seem to take some joy in the fact that she is now in a special club of people who know. I'm sure part of her is still sad, but also a bit excited that she can participate now in making magic for her little brother (4).

Anyway for those who passed this point with your kid(s), how'd it go?

reddit.com
u/zirconst — 4 hours ago
▲ 29 r/Unity2D+1 crossposts

Are you accessing and changing variables too much from outside a class? The dangers of getters/setters

Note: This is aimed more at beginners. Experienced programmers will likely know this stuff. But even the veterans among us might find something useful here.

The nature of Unity's component-based design can make it very easy for objects/classes to modify each other's variables (fields). For example, say we're making a dungeon crawler, and we're using some good design principles like having our Health in one component, our Equipment in another component, and our BattleStats in a third component.

This kind of code is very common:

private void AttackEnemy(Fighter target)
{
    int baseDamage = CalculateMyDamage();
    baseDamage -= target.CalculateMyDefense();
    target.myHealth.current -= baseDamage;
    if (target.myHealth.current < 0)
    {
      target.PlayOnDeathAnimation();
      int xp = target.CalculateEarnedExperience();
      myXPComponent.xp += xp;
    }
}

It's not terrible. We are intelligently using functions like CalculateMyDamage, CalculateMyDefense, and CalculateEarnedExperience rather than writing that stuff in our AttackEnemy function.

However, we're still tightly coupling the attacker and defender. The attacker shouldn't be responsible for checking to see if the defender is dead or not. It shouldn't be responsible for 'knowing' when to play the death animation. In fact, it shouldn't even be responsible for changing the defender's HP at all.

Because imagine if we now introduce damage from terrain. We make a new object called a Hazard, and it deals damage every second. If we keep writing code the same way, we might end up with:

private void CauseHazardDamage(Fighter target)
{
  int baseDamage = CalculateMyHazardDamage();
  baseDamage -= target.CalculateMyDefense();
  target.myHealth.current -= baseDamage;
    if (target.myHealth.current < 0)
    {
      target.PlayOnDeathAnimation();
      int xp = target.CalculateEarnedExperience();
      myXPComponent.xp += xp;
    }
}

You can already see that this is essentially duplicated from AttackEnemy, which is a red flag. For example, what if we want the roll for treasure when an enemy dies? Well, now we have to add code like this to both functions:

if (UnityEngine.Random.Range(0,1f) <= target.GetTreasureChance())
{
  Treasure reward = target.GenerateTreasure();
  // do spawn logic here
}

Then what if we want to add an effect to some Fighters where they have a chance to avoid a fatal blow? We might need to amend the code again for both functions:

  target.myHealth.current -= baseDamage;
  if (target.myHealth.current < 0)
  {
    if (target.HasStatus("avoid_fatal_blow") && UnityEngine.Random.Range(0,1f) <= AVOID_FATAL_BLOW_CHANCE)
    {
      target.myHealth.current = 1;
    }
    else
    {
      // regular 'on death' code
    }
  }

Or what if Fighters can have other status effects or items that react when they take damage? Suddenly, we have code that could look like this:

private void CauseHazardDamage(Fighter target)
{
  int baseDamage = CalculateMyHazardDamage();
  baseDamage -= target.CalculateMyDefense();
  target.myHealth.current -= baseDamage;
  
  if (target.HasStatus("reactive_damage_ability"))
  {
    // do some cool stuff here
  }

  if (target.myHealth.current < 0)
  {
    if (target.HasStatus("avoid_fatal_blow") && UnityEngine.Random.Range(0,1f) <= AVOID_FATAL_BLOW_CHANCE)
    {
      target.myHealth.current = 1;
    }
    else 
    {
      target.PlayOnDeathAnimation();
      int xp = target.CalculateEarnedExperience();
      myXPComponent.xp += xp;
      if (UnityEngine.Random.Range(0,1f) <= target.GetTreasureChance())
      {
        Treasure reward = target.GenerateTreasure();
        // do spawn logic here
      }
    }
  }
}

It just turns into a nightmare. Now there are a lot of ways to architect your code so that you don't mire yourself in scenarios like this. But for the purposes of this post, I want to focus on this idea:

If you find yourself directly getting, modifying, and setting variables that belong to other objects, this should tell you that you may be writing difficult-to-maintain code.

We could have realized this as soon as we wrote this line:

target.myHealth.current -= baseDamage;

Without going into excessive detail, a far more maintainable approach would be something like this.

private void AttackEnemy(Fighter target)
{
  // We can play VFX/SFX here...
  int baseDamage = CalculateMyDamage();

  // But we trust the TARGET to figure out what to do with the damage we calculated
  target.OnAttacked(this, baseDamage);  
}

private void OnAttacked(Fighter attacker, int baseDamage)
{
  int defense = CalculateMyDefense();
  baseDamage -= defense;
  OnDamageReceived(attacker, baseDamage);
}

// This logic is split out from OnAttacked, because we could certainly take damage from things
// OTHER than an 'attack'. For example, if we are poisoned, that might ignore defense completely.
// In that case we would just run OnDamageReceived(poisonDamage).
private void OnDamageReceived(Fighter attacker, int damageAmount)
{
  // This function SHOULD NOT know or care what each StatusEffect we have does.
  // We will trust the StatusEffects themselves to take this and modify it how they see fit.
  foreach(StatusEffect se in myStatusEffects)
  {
    damageAmount = se.OnDamageReceived(damageAmount);
  }

  // Our status effects may have reduced our damage to zero!
  if (damageAmount == 0)
  {
    // Play some kind of 'DEFLECT!' vfx and sfx.
    return;
  }

  myHealth.ReduceHealthFromDamage(attacker, damageAmount)
}
 
 // ---- now we are in the HealthComponent class -----

private void ReduceHealthFromDamage(Fighter attacker, int damageAmount)
{
  current -= damageAmount;
  OnHealthChanged();
  if (current > 0) return;
  OnTookLethalDamage(attacker);
}

private void OnTookLethalDamage(Fighter whoKilledMe)
{  
  // Like with OnDamageReceived, perhaps we have status effects that do crazy stuff IF we were to take lethal damage
  // We might run through them and exit if any of them bring us >0 again.
  foreach(StatusEffect se in myStatusEffects)
  {
    current = se.OnHealthReducedToZero();
    if (current > 0)
    {
      // Hooray, we survived somehow!
      OnHealthChanged();
      return;
    }
  } 

  OnDeath(whoKilledMe);
}

private void OnDeath(Fighter whoKilledMe)
{
  // ... give whoKilledMe rewards or something!
}

This isn't perfect, and there are many things we could do to improve it further, but nonetheless it separates our 'concerns' far better.

* If we want to add some kind of new block/parry mechanic, we just have to do it in one place: OnAttacked
* If we make new StatusEffects, we don't have to write any new code whatsoever in these functions
* If we want to change what happens on Fighter death, there's just one function that handles it
* If we add new sources of damage - traps, hazards, poison, cursed gear, etc - our existing functions handle it all seamlessly

... and so forth and so on! I hope you find this helpful. My goal isn't to prescribe a specific solution to code architecture as every game is different, but just to recognize overuse of getting/setting variables from outside the object or class as a potentially bad 'code smell'.

reddit.com
u/zirconst — 1 day ago

Digioh review - not really worth it

My company signed up for Digioh around April of last year on the promises of better email capture, forms, popups, and quizzes with the goal of increasing revenue both directly on-site and through email marketing. We're paying $1,000 per month. Their advertising is anywhere from 25 to 40x ROI. If we were getting anywhere near that much I'd be celebrating from the rooftops, but the reality has been quite different. Here's my review.

Positives

Their support and communication are quite good. Onboarding was smooth. Everyone I've talked to from there, either via email or in face-to-face meetings, has been cordial and knowledgeable. It's nice to interact with real humans.

The service itself is also functional. It does what it says on the tin. It allows you to configure lightboxes/popups, forms, and on-site quizzes. The building UI is clunky, but what you can do with it is pretty deep. If you're technical you can do just about anything in terms of routing input/answers to whatever internal logic you want. We typically pipe things directly to Klaviyo.

We use it for two on-site quizzes, several popups at various points in the funnel, and email capture w/ teaser. Our results are a lot closer to, maybe, 1.5x ROI. Let me get into why there's such a big delta between that and the promised results.

Questionable Results

First, their analytics (which connect with GA4) essentially claim the full value of purchases made after the email capture popup. Which on its face is reasonable... but then you remember that Klaviyo has email popups too. Keep in mind that people who enter their email in these are the highest-intent buyers so - of course - you'll get the most revenue from them. Our email capture rate with the Klaviyo popups alone was comparable to Digioh, so I would say on this point there's really no ROI because it's not beating what we could have had otherwise.

Then we have a few popups at other points in the funnel such as reminding existing customers of crossgrade deals, checkout abandonment, things like this. If we assume their revenue reporting is 100% accurate than the ROI of these would be something like... 6-7x. However I again have to question the methodology.

For example, we have a checkout abandonment popup that fires on exit intent from the checkout page with another email capture offering 10% off.

According to their reporting, 6% of visitors saw the popup this year and 94% did not. Of the people that saw the popup, only 3% filled out their email. That's 1334 people. That accounts for about... 1% of total people going through our Welcome series flow.

Rephrasing this, 1% more people signed up for our mailing list overall. But from this, Digioh claims attributed revenue of 1/5 the total Welcome series revenue from ALL sources. This to me is not plausible.

It's especially not plausible when I look at Digioh's reporting of attributed revenue to our main email capture popup, which claims a number that is higher than the overall revenue from our Welcome flow in Klaviyo in the same time period. It just doesn't add up.

Quizzes are OK, but...

Evaluating the effect of quizzes is somewhat easier because we have post-quiz email flows that go out to some % of people that take the quiz. We can look at Klaviyo-attributed revenue and see that number is in the ballpark of 1x ROI.

But the thing is... do we really need Digioh for this? Could we achieve the same thing with another quiz solution, maybe a custom WordPress plugin? I feel like we could - it might cost ~$5k to develop one that has the same functionality, but then we'd pay $0/mo after. There isn't anything about their platform that seems so technologically complex as to not be doable with custom code.

Bottom line

It could be that my company is simply not at the scale to really enjoy the benefits of Digioh, but we simply are not seeing anywhere near the results they promise (they literally say they guarantee 40x ROI in their site header) and I think that's largely because their attributed revenue numbers are inflated. Their customer service and strategy folks are great, but this doesn't make up for the fact that we could simply use Klaviyo native popups and custom quiz software to achieve the same thing.

If anyone else has used Digioh, I'd be curious to hear your experiences to see how (if at all) they differ from ours.

reddit.com
u/zirconst — 23 days ago
🔥 Hot ▲ 6.6k r/OneOrangeBraincell

We unfortunately could not resist adopting two dumb brothers, Tater & Tot. Send thoughts and prayers.

Last month we said goodbye to our beloved 15 year old gray tabby Tucker, leaving us with only our noble senior girl Moo Moo (15 year old black & white). We were not planning on adopting again anytime soon but then saw these guys on local social media, rescued from a nearby farm. We simply could not resist once we saw them in person. Please wish us luck

u/zirconst — 2 months ago

Good examples of affiliate programs for *premium* brands/stores?

I'm curious if anyone has examples of stores that have good (i.e. seemingly-effective and actually used) affiliate programs, particularly those selling niche and/or premium things. There are many, many services, apps, and plugins touting the benefits of affiliate programs... but I haven't encountered them much in the wild, other than high-visibility YouTubers with referral links.

For transparency, my own business uses one, but we only have a handful of affiliates total, and we maintain extremely close relationships with them. It's not open to the public. I'm considering expanding it but am very wary of doing so, because I've seen some other companies in our space go off the rails with affiliate programs that encourage users to spam links everywhere.

reddit.com
u/zirconst — 2 months ago

Goodbye Tucker (15). I thought we had more time.

I'm so sad. Our precious boy was so healthy for the first 14 years of his life. A few months ago he started losing weight, diagnosed with hyperthyroidism. We gave him meds. We thought he would he OK.

​

But within the last week and especially the last 24 hours he declined so fast. There was a trip to the ER, 5 more medications. He mostly isolated himself. We could tell he was suffering. We made the call to have him put to sleep at our house.

​

We made his last hours as good as we could. He's been an indoor cat but loves trying to escape to the yard, so we took him out there together. He didn't have much energy and mostly rested in the grass, but... his tail was up and he was purring. After, we cuddled him in our bed. He was always so tolerant of being picked up and hugged. We got pictures, we cried.

​

When the vet arrived we took him outside one last time. His last experience was trotting around in the clover, getting sleepy, and being held in our arms in the warm sun.

​

It all happened so fast. I miss him so much already. I want to share a few of the reasons why.

​

Tucker had one favorite toy, a gross, ratty, floppy ferret. He brought it around the house multiple times a day, meowing loudly. He especially liked doing this when one of our kids was crying like a gift for them- "Here human, maybe this will help!"

​

He greeted us whenever we entered the room with meows and purrs. He'd even come when you called his name. Sometimes, anyway.

​

He was obsessed with dairy, especially butter, and took every opportunity to lick some, even right out of the container if left unattended.

​

When he escaped outside he would intentionally hide and make it hard to get him back. But he never went further than about 50 feet from our house. Sometimes he would hiss at me when I went to retrieve him, but he'd always allow himself to he picked up, slung over my shoulder, and carried back inside.

​

For most of his life he would be ornery toward just about anyone other than my wife and I, saying away from them, hissing at would-be petters. With us he was a total baby. We felt like that contrast was special. Yet he managed to mellow out in his final few years, to the delight of friends and family.

​

He had a thing for shoes. Not ours, just random people like plumbers, electricians, or certain friends. He'd shove his face in there, nuzzle them, and even flop on them. It never failed to make us smile.

​

Despite being a pretty big and tall boy, comparable in size to his adopted sister Moo Moo, he was a pushover for her. He would always let her just push him away and eat his food, to our consternation. Except the handful of times he managed to get a piece of chicken on the bone. If he got one of those he went full goblin mode, gnawing and growling at anyone who interrupted his meal (just about the only time he ever growled)

​

He once managed to catch a bird during an escape attempt. He must have been very proud of himself. But he didn't kill it. Another time, a squirrel somehow made its way inside our house and Tucker probably had the best day of his life chasing it all over. I can only imagine, after seeing them taunt us from right outside the window for years, the joy in being able to let loose and hunt one.

​

There is so much more. I'll stop here though. I just wanted to remember him publicly. Thanks for taking the time to read this.

u/zirconst — 2 months ago

Our Facebook/Instagram ad freelancer is doing well... but do we need him?

Quick note: Please don't message me pitching your services.

I have a reasonably successful ecommerce business selling specialty software. Our only ad spend is on Facebook/Instagram, which has been the case for about 8 years. We've worked with various agencies but typically what happens is our ROAS sits at around the same number and I realize we are spending $X,000 to just tread water. We ended up working with a freelancer who charges $1500/mo plus a small % of ad spend, which for us averages around $13k/mo.

YTD we have a ROAS of 1.77x. This is the most important metric for my business. I don't make decisions based on CPMs, costs per "result" etc, just ROAS. And that's technically profitable, as our margins are good across the board. However it's also not really any better than we've done for the last few years, and every time he has tried to scale us up, ROAS drops.

Because we are in a hyper-specific niche, he is not able to produce creative for us. Every time he has tried the results are just not up to par due to brand voice issues or lack of technical expertise. I don't really blame him for that, but it means we have to be the ones making the creative, which we don't have a lot of time to do.

This year he has been playing around with different strategies such as bid caps, interest stacks, different campaign structures, testing previously successful ads etc. So he's not doing nothing. But it's also not clear to me if this is necessary work to simply maintain our (decent) results, or if it's more just a bunch of experiments that null out to nothing.

I would not mind cutting $1500-2000/mo out of our monthly spend. I could use that $1000 to pay a specialized content creator to make YouTube videos for us which our audience definitely loves (we have 18k subscribers).

That said, do I want to spend an hour per day mucking around in our account? Or can I basically listen to Meta's recommendations of having a couple broad campaigns all set to advantage +, dump new creatives in every so often, and hope for the best? Any perspective appreciated, thanks in advance.

reddit.com
u/zirconst — 3 months ago

I've spent the last 4 days doing extreme performance optimization on my site. Insights inside.

I'm posting this because I wish there was more information out there about how to deeply optimize WordPress/WooCommerce, beyond the generic advice of "use a caching plugin", "reduce plugin count", "switch to a faster theme", etc. Maybe it'll help someone else.

For context, my store has a few hundred products, around 40-50k unique monthly visitors, ~800k orders and ~700k customers. 10 years of data or so. We're on enterprise-grade hosting (previously Nexcess -> Kinsta -> now Rocket.net Enterprise I). Lots of plugins needed to dial-in our desired ecommerce logic, including around 14 custom plugins I've developed.

We were already using WP Rocket, Cloudflare Pro, and Redis prior to this optimization push. I was seeing load times of around 2.7s for unauthenticated users on product category pages, 2.4s on singles, GTmetrix "C" grade, ~1.5s TTFB, low cache hits, bad stuff.

I decided to try using Claude Code to help diagnose issues and work with me to fix them. I'm not an expert developer but I have a good understanding of PHP, MySQL, WordPress, WooCommerce, and related concepts, so I felt comfortable having CC do most of the heavy lifting while I guided & steered it.

In no particular order, here is what I (we) did. I'm going to try and find a balance of brevity with useful info here but I'm happy to elaborate on any points in the comments.

Complete, comprehensive scan of all plugin code and functions.php. I made a complete up-to-date local copy of wp-content and had Claude use an agent team (Sonnet) to pore of every single plugin and look for potential performance issues of "moderate to catastrophic" severity. This took around 30 minutes and gave me insights into particularly problematic ones (along with issues in my own custom plugins). Highly recommended. What you do with this info depends - in my case, I fixed a lot of logic in our own plugins, and also made some changes to commercial plugins we use, specific functions that were extremely heavy for no reason.

Of course, I now have to be careful with plugin updates because they might override my changes. But I have those changes stashed away in my local site copy, so I can always diff after an update to restore them. Worth it imo.

Use Code Profiler & Query Monitor to see specific issues. Static analysis is a good starting point but not the whole story. For example, some plugins are horribly inefficient... but if they are only ever used occasionally on the backend, that may have a low impact compared to a fairly efficient but 'hot path' plugin. This helped me figure out the absolute heaviest plugins to disable in the following step:

Use FreeSoul Deactivate Plugins to turn off plugins completely on specific pages. As an example, I use WooPayments and LearnDash. These are heavy as hell. In my case, they also don't need to be active on category pages or the home page. Despite having around 70 plugins total, I slimmed down the typical plugin load on key pages to more like 40.

Detailed probe of the website with lighthouse using Claude Code. I instructed another local instance to look at a given set of pages on the site and use tools like Lighthouse to identify and rank performance issues from the client side. This helped me figure out the heaviest payloads, such as my site apparently loading all of FontAwesome despite only using around 20 glyphs.

Use PerfMatters Script Manager to disable certain CSS/JS payloads. This somewhat overlaps with FDP, but not completely. For example, WooCommerce itself needs to be active almost everywhere, but not all of its CSS and JS does. With PerfMatters you can disable specific CSS/JS files on a page-by-page (or category) basis. Or, disable everywhere with exclusions.

Use subsets of fonts if necessary. I was using the entire FontAwesome set which was 100+kb. Apparently you can create a custom font with just the icons you need, which I did, reducing its total size to a fraction of that. Easy win.

Ensure Cloudflare is working as intended. I had Cloudflare Polish enabled but apparently it wasn't doing anything, because Super Bot Fight mode was blocking it. I was also inadvertently using a redundant plugin (ShortPixel). By authoring a simple exception in Cloudflare's managed rules, Polish started working as intended again, and I was also able to disable ShortPixel and save some resources there.

Another thing with Cloudflare is that we had Cloudflare Cache Reserve enabled but that wasn't doing anything either. Like it literally was caching nothing at all. I had to configure some Cache Rules to mark things actually eligible for caching, and thus take advantage of CF's speed.

Move 'pixel' type logic to Google Tag Manager if possible. We use Klaviyo for email, and so of course we have the Klaviyo plugin. Unfortunately, it has to be active on basically every page to capture page views (for potential automation triggers), add to carts, checkouts etc. It's a non-trivial performance hit. I used a simple MUPlugin to disable certain Klaviyo scripts and move that logic instead to Google Tag Manager, so events like page views, add to carts, and checkouts started could be captured without burning server resources.

Check and improve OPcache settings. This was crazy. I had never heard of OPcache before this; it's a PHP extension enabled on the server side that caches code as opposed to resources. When I looked at the runtime status of our OPcache install, it was completely maxed out on memory (128/128mb) and maxed out on the interned string buffer (8/8mb). As a result it was only caching about 54% of possible code. I talked to our host to triple its memory allocation and quadruple the string buffer. This brought us to 99% cache rate and literally shaved 300ms off TTFB right there.

Look for options table bloat - cruft / autoload. Plugins can accumulate junk in the options table which is in an extremely hot path in WordPress. The autoload size can get quite large, but even stuff outside the autoload can bog things down. In my case I had hundreds of thousands of junk entries from plugins like a mailer we haven't used in years and the official Facebook/Meta plugin, making the table hundreds of MB. Cleaning out unused stuff dropped it to <10mb, another easy win.

RESULTS:

Category pages went from 2.73s on an unauthenticated fresh load to 0.82s, 343mb memory to 238mb, 44 queries to 22, and 52k file I/O to 34k. Product pages and homepage had similar magnitudes of gains, as well as more cache hits (which load nearly instantly.) We went from a GTMetrix "C" with 66% performance to "B" with 85% performance, 185 -> 72ms total blocking time, 2.4s -> 1.5s LCP (it feels faster than that though.)

Hope this is useful or at least gets your gears turning!

u/zirconst — 3 months ago