65 Python Developer Jokes
I spent an hour debugging an IndentationError. It was one tab where there should have been four spaces. The file looked identical.
Python is the only language where pressing the spacebar wrong can take down production.
My code ran in Python 2. My code ran in Python 3. My code did not run in both at the same time. That took six years to admit.
The Python 2 to 3 migration started in 2008. I finished mine in 2023. I was not the last.
I typed pip install. Half the internet downloaded. The package I actually wanted was three dependencies deep.
requirements.txt said it worked on her machine. Pipfile said it worked on his machine. poetry said it worked yesterday. uv finally said it worked, and now nobody trusts it because it was too fast.
I created a virtualenv. Then I created a venv inside the virtualenv. Then conda activated something. I have not used my real Python in two years.
import this is a poem about clarity. The codebase I inherited has not read it.
Beautiful is better than ugly. Explicit is better than implicit. My coworker just imported everything with a star.
I asked about the GIL in an interview. The room went quiet. The interviewer poured himself a drink.
Threading in Python is a polite suggestion. The GIL is the parent in the room.
I gave the data scientist a CSV. She made it a DataFrame. I gave her a single integer. She made it a DataFrame. I gave her a coffee. I am now in row 0.
NumPy is not a library. It is a worldview.
I tried to write a for loop in front of a NumPy user. She closed her laptop and walked out.
I picked Django because it had everything. I picked Flask because it had nothing. I picked FastAPI because it had types. I am now maintaining all three.
Django: here is your admin panel, your ORM, your migrations, and your opinions. Flask: here is a function. Good luck. FastAPI: here is a type hint. Worship it.
The data scientist said tests slow her down. The model she shipped predicts that every house costs negative four hundred dollars.
I added type hints to the codebase. Everyone said thank you. Nobody ran mypy.
Type hints in Python are like seat belts in a parked car. Technically present.
The notebook has two hundred cells. None of them run in order. The kernel has been alive for nine days. We do not restart it. We have built a civilization on top of it.
I opened her Jupyter notebook. Cell 47 imports pandas. Cell 12 uses it.
I asked why a variable existed. She said it was defined in a cell she deleted in March. The variable is still in memory. It runs the company.
This used to work, he said, pointing at code that has never worked in any Python version that has ever existed.
I rewrote the loop as a list comprehension. It was three times slower. Nobody warned me. The book just said comprehensions are faster.
Nested list comprehension is read-only code. You write it once. You never read it again. The code reads itself, weeping.
I forgot to put __init__.py in the folder. Python refused to acknowledge the folder existed. The folder filed for divorce.
Then I added __init__.py and it still did not work because of the sys.path. I cried in a virtualenv.
What is the difference between __str__ and __repr__? One is for humans. One is for developers. I never remember which.
Duck typing means if it walks like a duck and quacks like a duck, it is a duck. In production, it is a goose with a duck costume and a NoneType in its pocket.
I read the asyncio docs. I read them again. I read them a third time. I wrote a synchronous version and shipped it.
async def, await, asyncio.run, asyncio.gather, asyncio.create_task, asyncio.ensure_future. I picked one at random. It deadlocked.
Decorators take three reads to understand. The first read, you panic. The second read, you almost get it. The third read, you write your own and inflict it on the team.
A decorator that takes arguments is a function that returns a function that returns a function. Nobody on the team knows which return is which.
Mutable default arguments. The footgun that ships with every Python install.
def add(item, items=[]). I will let you discover the rest yourself.
I imported pandas at the top of a script that does not use pandas. The cold start went from 0.2 seconds to 1.4 seconds. The script runs in a cron every minute. I am the reason for the cloud bill.
Someone in the standup said dunder. Half the room nodded knowingly. The other half pretended to nod knowingly.
I caught an exception with bare except. Six hours later I learned the keyboard interrupt was being swallowed.
ImportError: attempted relative import with no known parent package. This is the error that ends careers.
f-strings made Python beautiful. Then someone discovered f-strings can run arbitrary expressions, and now my logs contain function calls that have side effects.
Pythonic means whatever the loudest person on the team says it means today.
I shipped a one-line lambda. The code review comment said this is not Pythonic. I rewrote it as a four-line function. The code review comment said this is too verbose. I quit and learned Go.
The walrus operator landed in 3.8. I hated it. I refused to use it. Two years later I wrote (n := len(items)) without thinking. The walrus had won.
I added a match statement to a codebase. The PR sat for nine days. The reviewer said he would learn it next sprint. He has not learned it next sprint for three years.
Structural pattern matching is elegant. It is also the only Python feature I have to look up every single time. The elegance has a cost and the cost is my memory.
List, tuple, set, frozenset. I picked tuple because I read somewhere it was faster. The function mutates the collection in three places. The tests do not run.
a is b returned False. a == b returned True. The junior on the team submitted a five-page postmortem. The senior said welcome to Python.
if x is None. Not if x == None. The linter will tell you. The reviewer will tell you. Your dreams will tell you.
Someone wrote True = False at the top of a Python 2 file. The bug took a week to find. Python 3 made True a keyword. I owe the core developers a beer.
A new hire wrote print without parentheses and looked surprised when nothing happened. He had not touched Python since 2011. I welcomed him to the future.
10 / 3 returned 3.333. 10 // 3 returned 3. The data pipeline rounded a million rows the wrong way. The auditor was not amused.
range(10) does not return a list anymore. I learned this by trying to index into it and getting a TypeError. I have been writing Python since 2009.
Dicts preserve insertion order since 3.7. I built a feature around this. It worked. The senior reviewer said do not rely on it. I sent him the PEP. He stopped responding.
I replaced a list comprehension with a generator expression to save memory. The function downstream called len on it. The function crashed. The memory was fine. The career was not.
yield from is one of those features that looks magical until you need to debug it. Then it is just from, with extra steps.
I wrote a context manager with __enter__ and __exit__. It worked. I rewrote it with @contextmanager and a yield. It also worked. I do not know which one is correct anymore.
@dataclass replaced 40 lines of boilerplate with one decorator. Then someone wanted a custom __eq__. We added 38 lines back.
NamedTuple, dataclass, TypedDict, pydantic, attrs. Five ways to do the same thing. The team uses all five. In the same module.
I opened the typing module to look up one thing. Two hours later I was reading about ParamSpec and TypeVarTuple. I never found the thing I was looking for.
mypy said the type was wrong. The runtime did not care. The runtime never cares. mypy is a yelling shadow.
A junior committed a .pyc file. Then a __pycache__ folder. Then the entire venv. The PR was 12,000 files. The CI ran for an hour.
I added sys.path.insert(0, '..') to make an import work. Three years later the entire app loads in the wrong order and nobody knows why. It was me. It was always me.
The conftest.py exists. It just exists nowhere near where you expect. The fixture you need is in a conftest two directories up that the docs never mentioned.
I wrote a pytest fixture that depended on a fixture that depended on a fixture that depended on the first one. Pytest told me about it. Politely.
The data team shipped a Pandas UDF that runs for four hours on 200 rows. I rewrote it with iloc instead of loc. It runs in 11 seconds. Nobody asked me. Nobody thanked me.
Why Python humor is its own dialect
Python jokes hit differently because the language wears its quirks on its sleeve. The whitespace is the syntax. The packaging story is an open wound that everyone has a scar from. The GIL is a single acronym that triggers a sigh from every backend dev who has tried to use threads. And the Jupyter notebook with cells executed out of order is not a bug, it is a way of life for half the data science world. You laugh at Python jokes because you have lived inside the same paradoxes: a language famous for being readable, written by people who haven't read PEP 8 since 2014.
JavaScript Jokes Jokes
I asked JavaScript what type null was. It said object. I stopped asking questions.
NaN is not equal to NaN. That is the only thing in JavaScript that knows itself.
I used == once. The linter is still in therapy.
Hoisting is when your variable shows up before you do.
I ran npm install on a Monday. By Friday it finished, and three of the packages were already deprecated.
My node_modules folder has its own gravitational pull. Light bends around it.
The error said Cannot read properties of undefined. I cannot read properties of my life either, JavaScript. We are the same.
undefined is not a function. And yet, here we are, calling it.
I learned a new framework on Monday. On Tuesday it was legacy.
Callback hell is when your code starts looking like a staircase to nowhere.
Then async/await arrived. Now my code looks normal and fails silently.
Promise.all is a great way to fail eight things at once.
I tried to sort an array of numbers. [1, 2, 10, 20] became [1, 10, 2, 20]. JavaScript sorts numbers like a four year old reads a phone book.
this in JavaScript depends on the weather, the position of the moon, and whether you used an arrow function.
Arrow functions fixed this. They also broke this, in different code, in ways you will discover later.
I asked a junior what the prototype chain was. He said it was a bike lock. I did not correct him. He will figure it out at 4:20am like the rest of us.
JavaScript has block scope, function scope, and one more scope nobody talks about: the scope of regret.
I added a Date object to my code. Now it is January 1970 and I am eight months old.
Date math in JavaScript is what happens when nobody on the original team owned a calendar.
I ran into a package called left-pad once. It was eleven lines of code. Half the internet went down when its author left.
There is a JavaScript framework released every Tuesday. The other days are for arguing about it on Twitter.
I picked Webpack in 2017. I picked Vite in 2022. I am picking again next year. I have not shipped a feature since 2016.
Babel transpiles your modern JavaScript so that one user on Internet Explorer can still hate your site.
ES6 came out and we all rewrote everything. Then ES2017, ES2020, ES2022. The rewrite is the deliverable now.
jQuery is dead. It still ships on more sites than React. Death suits it.
I deleted node_modules and ran npm install again. It is the JavaScript version of turning it off and on. It works about as often.
package-lock.json has merge conflicts on every pull request. Nobody reads them. We just accept incoming and pray.
I once read the entire package-lock.json file. My will is being contested.
JavaScript Object Notation was invented by someone who thought, what if we made JavaScript portable, but with even fewer features.
JSON does not support comments. This was a feature, apparently. The reasoning has not aged well.
The event loop is the only thing in JavaScript that actually does its job on time.
setTimeout(fn, 0) is not zero. It is whenever the event loop feels like it. JavaScript runs on island time.
Closures are simple. A function that remembers things. Like an ex with your Netflix password.
I declared a variable with var inside a loop. It escaped. It is roaming the scope. I cannot find it.
JavaScript fatigue is when you read a tutorial that mentions four tools you have never heard of, and one of them replaces the tool you learned last month.
TypeScript is JavaScript with feelings. Mostly the feeling that you should have used TypeScript from the start.
I added strict mode to a legacy file. The build took six hours and one of the founders cried.
I tried to compare two objects with ===. JavaScript said no. They are different objects. They are identical. They are different. Welcome.
[] + [] is an empty string. [] + {} is [object Object]. {} + [] is 0. The language is held together by hope.
I have been writing JavaScript for 25 years. I still google how to copy an array. The answer changes every two years.
I used JSON.parse(JSON.stringify(x)) to deep clone an object. It worked. The Date became a string, the function vanished, the undefined keys left. Otherwise, identical.
I wrote a destructuring line so dense the reviewer asked if it was a regex. It was assigning three variables.
for...in iterates keys. for...of iterates values. The one you picked is always the other one.
I spread an array into another array, into another array, into another array. The linter shrugged. The bundle did not.
parseInt without a radix is a coin flip dressed as a function.
I hit Number.MAX_SAFE_INTEGER in production. The id became approximately the id. The user became approximately the user.
BigInt and Number cannot be compared with strict equality. They also cannot be added. They live in the same house and refuse to speak.
I added optional chaining to one expression. Now every expression has it. The file looks like a question I never finished asking.
The ?. swallowed an error for six months. We found it the day before the demo. It had been eating money.
I wrote an async function and forgot to await it. The Promise sat in a corner, fulfilled, ignored, like a wedding gift from your aunt.
Promise.all rejects on the first failure. The other nine requests still ran. You paid for them. You will not see them.
ts-ignore is the prayer of a tired engineer. @ts-expect-error is the prayer of one who has learned shame.
TypeScript any is the trapdoor under the trapdoor. You meant to type it. You will. Next sprint.
I installed a library. Its .d.ts file described a different library, one I would have preferred.
ESLint says no semicolons. Prettier adds semicolons. The CI runs both. The branch is on fire and we are arguing about punctuation.
The import order plugin reordered my imports. The diff is 800 lines. The change is zero lines. The reviewer is upset.
I forgot a semicolon before a line starting with a bracket. ASI saw the bracket and assumed the previous line was a function call. The previous line was a return statement.
I wrote return on one line and {} on the next. The function returned undefined for three years. Nobody noticed because nobody read the return value.
fetch landed in Node 18. My code targeted Node 16. The polyfill targeted Node 14. The container targeted Node 20. The deploy targeted my weekend.
There is a deprecation warning in our logs from 2019. We scroll past it the way you scroll past a relative on a flight.
One dependency in our tree pins Node 14. We pinned the whole project to Node 14 to keep it happy. The project is also pinned to 2021.
I refactored a 4000 line module. Every console.log survived. They are the only thing that did.
use strict at the top of a file in 2026. It is a relic. Like a thank you note. Nobody asked for it. It is correct anyway.
I wrote an IIFE in front of a junior. She asked why the function was eating itself. I could not explain in a way that did not make her sad.
I found var in a 2024 codebase. It was load bearing. Removing it broke three pages. We left it there. It has earned its place.
Date.now() and new Date().getTime() return the same number. The codebase uses both. The team has split into factions. The standup is now diplomacy.
I set a setTimeout for 1000ms. It fired at 1003. I lost a test. I am writing this from the postmortem.
I called event.stopPropagation in the handler. The form submitted anyway. There were six handlers. I had stopped exactly one of them.
Defaulting with || worked until a user typed 0. Then I learned about ??. Then I learned about a different bug I had been hiding for two years.
Sparse arrays exist. I did not know this. The array had length 7 and three of the slots were holes. Not undefined. Holes. The shape of the missing thing.
CSS Jokes Jokes
I centered a div. My team threw a parade. The parade was also not centered.
There are nine ways to center a div. I have tried all of them on the same div, in order, and the div is now somewhere in Belgium.
z-index: 9999 was not enough. z-index: 99999 was not enough. The element behind it had position: static. I had been fighting a ghost.
I added !important. Then the next developer added !important. Then a third. The stylesheet is now a screaming contest.
Specificity is just CSS doing math you did not consent to.
I wrote one class. The inline style overrode it. The !important on a sibling overrode that. The browser default came back through a child selector. I went home.
Flexbox solved everything. Except the one layout I needed, which was a grid. So I learned grid. Now I use both, in the same component, fighting each other.
Margin collapse is when two margins meet and decide one of them was enough.
I set margin-top: 20px on a child. It pushed the parent down instead. I have read the spec four times. The spec is correct. I am the one who is wrong.
The design said 16px. The browser rendered 15.99996px. The QA tester opened a ticket. I closed it. They reopened it. We are now in mediation.
I used vh on mobile. The address bar moved. My footer is now in the next time zone.
100vw includes the scrollbar. I did not know that. The horizontal scroll has been there since 2019.
I wrote position: absolute without a positioned parent. The element went to a place I did not know existed. It is making friends there.
The button looked perfect on my machine. My machine is a 27 inch monitor. The user is on a phone. The button is now the size of a postage stamp.
I learned CSS Grid. Then I tried to use it on a real project. I went back to flexbox inside flexbox inside flexbox.
Subgrid landed. Three browsers shipped it. The fourth shipped it last year. We supported the fourth. We could not use subgrid for three years.
I asked the designer for the spacing. They said vibes. I shipped vibes. The PM opened a ticket about the vibes.
I once nested a selector seven levels deep. It took an hour to override one color. I quit and joined a different team. The selector is still there.
CSS in JS exists. CSS modules exist. Tailwind exists. Plain CSS exists. We picked all four in the same repo. Onboarding takes a week.
Tailwind says use utility classes. My HTML now reads like a license plate auction.
I tried to vertically align text in a button. The text moved one pixel. The button grew two. The page reflowed. The deploy failed.
display: inline-block adds a mysterious gap. The gap is whitespace in the HTML. I have known this for ten years. I forget every time.
I floated an element in 2024. The junior asked why. I said it was the only way I knew. She wrote a flexbox version. It worked. I went outside for a minute.
clearfix was a class. Then a hack. Then a Sass mixin. Then a relic. My codebase still has all four.
The bug only appears in Safari. The Safari devtools work on a Mac. The bug is reported by Windows users. I do not have a Mac. The bug is permanent.
I wrote width: 100%. The element overflowed by exactly the padding. I learned about box-sizing that day. I have written box-sizing: border-box at the top of every project since.
Pseudo-elements are real elements that the spec refuses to acknowledge.
I styled ::before and ::after. The content property needed a value. I gave it an empty string. The element appeared. I closed the laptop and walked away.
calc() is a function that lets you do math inside CSS. It is also a function that lets you do math wrong inside CSS.
I wrote calc(100% - 20px). The element was off by a pixel. I changed it to calc(100% - 21px). Now it is off by a pixel in the other direction. This is my life.
Custom properties are variables. They cascade. They inherit. They can be set inline. The Sass team has been quietly winning a war they did not know they were in.
I named a variable --primary-color. The brand changed. I now have --primary-color set to the secondary color. The secondary color is the old primary. Nobody on the new team can read the stylesheet.
I used em instead of rem. Now the font scales with the parent. The parent scales with its parent. The h1 inside three nested divs is the size of a thumbtack.
rem is em but for the root. The root is html. The html font size is 16px unless someone changed it. Someone changed it.
I tried to animate height from 0 to auto. CSS said no. CSS has said no since 2010. CSS will say no in 2030.
transition: all is a love letter from past me to the GPU. The GPU read it and filed it under regrets.
I added a transition on background-color. It ran on every property the element had. Including the ones I did not know it had. The page now breathes.
Keyframes are easier than they look. Until you need to chain them. Then you need a state machine. Then you need a JavaScript animation library. Then you wonder why CSS exists.
I used will-change on every element. The browser ran out of will. The page froze. I learned moderation. Then I forgot.
Media queries are how you find out your design does not work on a tablet.
I wrote a mobile-first stylesheet. The desktop overrides are now longer than the mobile rules. I think I have invented desktop-second.
Container queries shipped. The team has been writing container queries for three months. The codebase has zero container queries. We are still writing media queries. Habits are heavy.
I wrote @supports for a property. Every browser supported it. The fallback path has run zero times. It will run forever.
Print stylesheets exist. I have written one. The user printed the page. They emailed me a photo of the printout. The footer was on a second page. They wanted it on the first.
I styled a form. The select element refused. The select element has been refusing since 1996. The select element will outlive us all.
appearance: none is a prayer. The browser hears it. The browser ignores half of it. The select still looks native on Safari.
I asked a senior for help with a layout bug. She opened the devtools, deleted three properties, and the bug was fixed. I had added those three properties yesterday. She did not say anything. She did not have to.
There is a stylesheet in our repo from 2014. Nobody knows what selectors it applies to. Deleting it breaks the homepage. Keeping it breaks the about page. We have learned to live with this.
I wrote a one-line CSS fix. It was reviewed by three engineers, blocked by design, approved by the PM, blocked again by accessibility, approved again, and merged six weeks later. The bug was a typo.
Reset stylesheets exist because browsers cannot agree on what a button is.
I used normalize.css. Then reset.css. Then sanitize.css. Then I wrote my own. Then I deleted it and went back to normalize. The circle is round.
The design system has 200 components. We use four. The other 196 are documented, tested, and unused. They are the design system's retirement plan.
Sass nesting was a great idea. Native CSS nesting shipped years later. The Sass team had been right the whole time. CSS apologized in the form of a spec.
I wrote pointer-events: none on an overlay. The overlay no longer caught clicks. The button under it caught clicks. The form submitted. The user was charged twice. I learned about overlays the hard way.
I have centered the div. It is centered on my screen, in this browser, at this zoom level, on this OS, with this font installed. Tomorrow it will not be centered. The div was never the problem. The div was the lesson.
PHP Developer Jokes Jokes
PHP is dead. It has been dead every year since 2004. Currently the corpse is powering roughly 75% of the web.
I told a Node developer I write PHP. He said, 'I'm so sorry.' I said, 'My salary isn't.'
Why is needle, haystack the argument order in strpos but haystack, needle in in_array? Because PHP was written by twelve different people who never met.
'0' == false is true. false == null is true. null == '0' is false. This is not a bug. This is a feature. This is also why we have ===.
A PHP developer walks into a bar, a Bar, a BAR, and an Object of type Bar. All four get served.
My favorite part of PHP 8 is being able to tell people I write modern PHP without lying.
Composer install: a chance to make coffee. Composer update: a chance to question your career.
Why do PHP developers love the question mark? Because `?->`, `??`, `??=`, and `?:` are the only operators that have ever truly understood us.
I named a variable $data. Six months later I came back and asked, 'data of what?' Past me did not leave a forwarding address.
PHP 5 to PHP 7 was a free 2x performance upgrade. My boss's response: 'Great, now we can run twice as many WordPress plugins.'
The `<?php` tag is mandatory. The `?>` tag is optional, eats trailing newlines, and has ruined more headers already sent errors than any other character in computing.
Why don't PHP developers play hide and seek? Because $_GET always finds you.
array_map(callback, array). array_filter(array, callback). array_reduce(array, callback). PHP: pick a lane. PHP: no.
Haters who haven't written PHP since 2010 are like film critics who walked out of Star Wars and never saw the rest of the saga. Yes, the prequels were rough. We're on the good ones now.
I switched from array() to [] in 2014. My senior dev called it a fad. He also still types out array() in 2017.
What's the difference between mysql_query, mysqli_query, and PDO? About fifteen years of you not updating that one legacy app.
PSR-12 is what we call it. My team's style guide is what we actually use. These two things are unrelated.
I asked the Laravel developer how he handles dependency injection. He said, 'The framework does it.' I asked the Symfony developer how he handles dependency injection. He handed me a 400-page book.
Before namespaces, we had Class_Name_With_Underscores_Forever. We called it the PEAR convention. We also called it the reason we drank.
My favorite PHP error: 'Cannot use object of type stdClass as array.' My second favorite: 'Cannot use array as object.' My third favorite: just looking at the data and not knowing which one I have.
JIT in PHP 8 made my benchmarks 20% faster and my CRUD app 0% faster. Worth it for the bragging rights.
Opcache is the unsung hero of PHP. It is also the reason your code changes don't take effect until you wonder why your code changes aren't taking effect.
I added type declarations to every function in the codebase. Three weeks later, we had caught more bugs than the previous two years of unit tests. The previous two years of unit tests had zero coverage. The bugs were still real.
Why did the PHP developer get into trouble with HR? He kept telling people he was loosely typed.
'PHP isn't a real language.' Says the developer whose framework wraps a templating engine that compiles to a language that runs on a runtime that ships with twelve flavors of `Date`.
PHP 5.6 reached end of life in 2018. The number of production servers running it in 2017? Also, 'yes.'
I opened a ten-year-old WordPress plugin. It had no namespaces, no Composer, no PSR-anything, and exactly one global called $wpdb. It also worked perfectly. That is the WordPress codebase in one anecdote.
A junior asked, 'Why do we still use PHP?' I showed him the W3Techs CMS share chart. He asked, 'Why do we still use WordPress?' I showed him my mortgage.
echo and print are both ways to output text. echo is faster. print returns a value. Neither one was the wrong choice. Both of them were.
Why do PHP developers love sandwiches? Because the `<?php` is the bread, the `?>` is the bread, and the HTML in the middle is the lettuce we definitely should have moved into a template engine.
I checked the PHP manual. There were 2,847 user-contributed comments on the strtotime page. None of them explained why 'next tuesday' on a Tuesday returns next next Tuesday.
Attributes arrived in PHP 8. For twelve years before that, we used docblock annotations and a regex parser called Doctrine. We didn't talk about it.
Why don't PHP developers trust stairs? Because they're always coercing them up and down.
I told the Go developer that PHP added named arguments. He asked, 'Wasn't that always there?' I showed him the changelog. He aged a year on the spot.
PHP-FPM is what powers your site. Mod_php is what your shared host is still secretly using in 2017. This is fine.
$_POST contains everything from the form. $_GET contains everything from the URL. $_REQUEST contains both, plus cookies, plus a lawsuit.
The composer.json says PHP ^7.2. The lock file says PHP ^7.2. The Dockerfile says PHP 8.1. The production server says PHP 5.6. Welcome to onboarding.
I asked the architect why he chose PHP for the new project in 2017. He said, 'Three reasons: I can hire for it, I can deploy it, and it's already faster than half the alternatives.' Then he muttered, 'Also Laravel is genuinely good now.'
Why is the elephant the PHP mascot? Because it never forgets the time you wrote register_globals = On.
'PHP is fine actually' is the only honest tech blog post title of the last decade. It also gets exactly zero shares from people who haven't written PHP since 2010.
How many PHP developers does it take to change a light bulb? One. But first he has to check if it's `null`, `''`, `0`, `'0'`, `false`, or an empty array. They all evaluate as falsy and exactly one of them is the actual bulb.
The Symfony console command finished in 0.04 seconds. The `composer install` that preceded it took four minutes. This is the eternal ratio of PHP work.
I wrote PHP for fifteen years before I learned about the spaceship operator. It was added in PHP 7. I had been writing PHP 7 for two years.
I forgot htmlspecialchars on one form field in 2014. I still get LinkedIn requests from the pentester who found it.
The `@` error suppression operator is the duct tape of PHP. It does not fix the leak. It just makes sure nobody hears it.
How do PHP developers debug? var_dump, die, refresh, repeat. The junior calls it primitive. The senior calls it production-grade.
var_dump shows the types. print_r shows the values. dd() shows both, kills the request, and dumps you straight into a Tailwind error page. Guess which one I added to the global namespace of every project I have ever touched.
PHP magic methods are called magic because nobody knows when they fire and everybody is afraid to ask. Look up __call once a year. Forget it again by Friday.
The `use` keyword imports a namespace. The `use` keyword imports a trait. The `use` keyword captures a variable into a closure. Three jobs, one word. PHP saw what JavaScript did with `function` and said, 'Hold my beer.'
I spent four hours debugging a Symfony service container issue. The fix was one line in services.yaml. The lesson was nothing. I will do it again next month.
Phinx vs Doctrine migrations vs Eloquent migrations. We picked one, switched twice, and now have three migration tables in the same database. Production has never been more honest about itself.
Every PHP team has a six-year conversation that starts with, 'We should just rewrite this in Go.' The conversation outlives the team. The PHP app outlives the conversation.
I ran du -sh on the repo. App code: 47 MB. vendor: 312 MB. The vendor directory is not part of the project. The vendor directory is the project. The app code is just the part we wrote between Composer installs.
Nobody has touched php-fpm.conf in this repo since 2019. The pm.max_children value is 5. The server has 32 cores. We do not speak of it. The site is up.
I ran php artisan in production exactly once. It put the site in maintenance mode at 2pm on a Tuesday and I never recovered the trust of the ops team. artisan stays on my laptop now.
React Developer Jokes Jokes
I put a setState inside a useEffect with no dependency array. My laptop fan is now a percussion instrument.
The eslint plugin told me my dependency array was missing a value. I added the value. Now I have an infinite loop. The linter is correct, technically, and also responsible for my breakdown.
My React app has three state managers. Context for the easy stuff, Redux for the legacy stuff, and Zustand for the parts the new hire wrote last week.
I called a hook inside an if statement once. React called my mother.
Prop drilling is just inheritance, but you have to type it out at every floor.
useMemo, useCallback, memo. Three tools for solving a performance problem you do not have, while introducing four new bugs you definitely will.
There is a class component in our codebase. Nobody wrote it. Nobody can delete it. It has been there since the React 15 era and it still ships.
Server components, client components, server actions, client actions, use client, use server. I used to be a developer. Now I am a directive technician.
I shipped a Next.js app and the build artifact is 4 MB. The user is on a phone in a coffee shop. The phone is fine. The coffee shop wifi has filed a complaint.
React Fiber sounds like a cereal that is good for your bundle size. It is not.
The hydration mismatch error told me the server rendered 12:04 and the client rendered 12:05. I fixed it by lying about the time.
An Error Boundary is what you call the wall between you and the part of the app you do not want to debug today.
Suspense is React's way of saying, I will get to it, please stop asking.
I added forwardRef to a component, then memo, then displayName, and now the file is mostly ceremony with a small div inside.
Controlled inputs, uncontrolled inputs. Pick one. Then pick the other six months later when you remember why you picked the first one.
I gave a list of items the index as a key. React rendered them in the order I asked. The state on each row did not get the memo.
useEffect with an empty dependency array is React's componentDidMount. We added three more hooks and called it a paradigm shift.
The React team announced a new way to fetch data. That is fine. We have only had eleven of them.
I tried to call useState inside a forEach loop. The hooks rules took my keyboard away and slid it across the room.
My component re-rendered six times on initial mount. I added useMemo. Now it re-renders six times and has a memo.
A junior asked me why we use Redux. I started talking. They left to get coffee. They came back. I was still talking.
Zustand is Redux for people who read one blog post and decided that was enough.
Jotai is Zustand for people who read two blog posts.
Context API is fine for theme and auth. Then someone puts the entire shopping cart in it. Then the whole tree re-renders when you change the font size. Then you write a postmortem.
Vite starts in 200 ms. Next.js starts in 18 seconds. We picked Next.js because of the routing. We have four routes.
Remix told me to use the platform. Then Remix joined React Router. Then React Router joined the conversation about whether anything is the platform.
I ran React DevTools. It told me my entire app re-renders when the user blinks.
I wrapped a component in memo to skip re-renders. I pass it an object prop. The object is a new reference every render. The memo is a decoration now.
useCallback inside a parent that re-renders every keystroke is just a more expensive arrow function.
I asked the senior dev how to fix prop drilling. They said composition. I asked what composition meant. They said it depends. I have not slept since.
The eslint exhaustive-deps rule and I have a relationship. It yells. I disable it. It yells. I add the dep. It loops. We start over.
Premature optimization in React is wrapping every component in memo and then deploying a 6 MB bundle full of memoization metadata.
I opened a 2019 React tutorial. It was wrong about everything except the JSX syntax, and even that has opinions now.
I tried to render a list without a key. React did not yell. It just made every checkbox check the wrong row for the rest of the user's session.
Server components do not have useState. Client components do not have async. The components that have both are called pages, and they live in Next.js.
The use client directive at the top of a file is the React equivalent of saying, fine, do it your way.
I migrated a class component to a functional component with hooks. The diff was negative four lines. The behavior was negative two features.
useReducer is what you reach for when useState gets crowded and you still refuse to install Redux.
The React docs got rewritten. They are excellent now. The old StackOverflow answers are still the top result for every search.
I wrote my first React component in 2015. It was a class. It worked. I rewrote it in 2018 as a hook. It worked. I rewrote it in 2024 as a server component. It works on the server. The button does nothing.
Every React project is one useEffect away from being a chat app and one Suspense boundary away from being a framework.
The framework's name is React. The mental model is reacting. The job is mostly waiting for it to stop reacting.
Suspense was announced as a feature, then as a pattern, then as a primitive, then as a marketing term. It is now all four, and I still wrap it in a try-catch out of habit.
Server actions let me call a backend function by importing it. The network tab still shows a POST. The abstraction is leaky and I am grateful for the leak.
I used the use hook to read a promise inside a component. It worked. I do not know why it worked. The docs say I should, and I believe them in the way one believes in weather forecasts.
The React docs are objectively excellent. I know this because every blog post that gets the answer wrong starts with, the docs were unclear, so I wrote this.
create-react-app was deprecated quietly enough that half my team is still running npx create-react-app on greenfield projects in 2026.
Migrating from CRA to Vite took an afternoon. Migrating the team's muscle memory away from npm start took six months.
We have been almost done migrating to the App Router for eighteen months. The pages directory is the load-bearing wall of the entire product.
Every developer forum has the same thread. Should I use Next.js or Remix. The top answer is from 2023. The framework named in the answer no longer exists in that form.
React Query became TanStack Query because it also does Vue now. My imports still say react-query. The codemod is on the backlog. The backlog is also on the backlog.
The styling debate goes styled-components, then CSS modules, then Tailwind, then vanilla-extract, then back to plain CSS because the new hire asked what the point of all this was.
The framer-motion animation on the empty state is more polished than the feature it is hiding.
Storybook is where the design system lives. The design system is where the components nobody uses in the app live. The app uses a div with a className.
Every CI run flags 47 npm audit warnings. Four are exploitable in theory. Zero are exploitable in our build. We ship anyway and call it acceptable risk.
React 18 told me to import from react-dom/client. Half my tests broke immediately. The other half broke when I fixed the first half.
I miss Enzyme the way I miss flip phones. Not enough to use one again, but enough to bring it up at parties.
React Testing Library asks me what the user would do. The user would close the tab. That is not a valid query.
The React DevTools profiler is the most powerful tool I have opened exactly twice. Once to learn it existed. Once to confirm it still does.
Someone opened a PR titled, this component re-renders on every keystroke. The fix added three useCallback wrappers and one useMemo. The component still re-renders on every keystroke, but now the diff looks busy.
MongoDB Jokes Jokes
"Is MongoDB web scale?" "Yes." "What does that mean?" "Nobody has ever said."
I love MongoDB. I love it so much I migrated to Postgres.
"It's schemaless." The schema is in the application now. In fourteen places. Slightly different in each.
MongoDB at 4 a.m.: "primary stepped down." Me: why.
The collection had 14 different shapes for the same field. The field was "id."
"We chose MongoDB for flexibility." Three years later we are writing a migration script for every document.
I wrote a query. Mongo accepted it. Mongo also did not run it. The field name had a typo. No error. No warning. No results.
The aggregation pipeline has 11 stages. I understand three.
"What's the data type of this field?" "Yes."
Sharding setup: three config servers, four shards, two mongos routers, and one engineer crying in a corner.
The Jepsen report came out. We pretended we did not read it.
"Why is this query slow?" No index. There's never an index.
MongoDB's strongest feature: the marketing.
"We don't need joins." The application now does the joins in JavaScript, badly, four times per request.
ObjectId. That's it. That's the joke.
I picked the wrong shard key in 2017. I think about it every day.
"It's eventually consistent." Eventually is doing a lot of work in that sentence.
The replica set election took 47 seconds. The application timed out at 30. The users learned about consensus algorithms whether they wanted to or not.
"Mongo is fast." It is. It writes nothing very quickly.
Our document grew past 16 MB. We split it. Then we joined it in code. We reinvented foreign keys, badly.
"You shouldn't store everything in one collection." We stored everything in one collection.
The write concern was "acknowledged." The write was not.
I asked for an aggregation example. The answer was a $-sign keyword I had never seen before.
MongoDB Atlas: your data, our data centre, their bill.
"Do we have backups?" "Mongo has a dump command." "Do we run it?"
The schema migration was "add a new field with a default." The field is on 12% of documents three years later.
I learned BSON exists. I learned it has a date type. I learned the date type is silently wrong in 1970.
"Mongo handles unstructured data." The data became structured the moment we tried to query it.
The .explain() output is 800 lines. The useful information is on line 612.
Our developer onboarding said: "You don't need to know SQL." Our developer onboarding lied.
"Just denormalize." The word that explains every Mongo schema decision and every Mongo schema regret.
The Mongo connection string has 14 parameters. I know what two of them do.
I added a unique index. Mongo built it for nine hours. During those nine hours, writes paused. Nobody warned anyone.
"What's the difference between a database, a collection, and a document?" "Yes."
Mongo's default write concern used to be "don't wait." It explains a lot.
The interview question was: "Why did you pick Mongo?" The candidate said: "It was on the architecture diagram when I joined."
We have one Mongo cluster. It holds the users, the events, the audit log, and the cache. It is also the reason none of those work.
$lookup is the join we said we did not need.
"MongoDB is ACID now." For multi-document transactions, with caveats, in some configurations, after version four, if you ask nicely.
I joined a new company. They use Mongo. I sighed in a way they recognised.
The Mongo dashboard is beautiful. The Mongo dashboard hides the truth.
"We're moving off Mongo." The sentence I have heard at four companies, in three of which it was true, in two of which it actually happened.
The query hit no index. It scanned the collection. The collection had 400 million documents. The page rendered eventually.
Mongo's tutorial uses a blog as the example. Nobody has ever built a blog with Mongo.
"What's a primary key in Mongo?" "_id." "Why the underscore?" "Nobody alive remembers."
Elasticsearch Jokes Jokes
"Is the cluster green?" "It's yellow." "Again?" "Still."
Elasticsearch promises near real-time search. Near is doing a lot of work in that sentence.
"Just add more shards." Famous last words before a mapping explosion.
The cluster was healthy until somebody pushed a new index template.
"We use Elasticsearch as our primary database." And that's how my afternoon disappeared.
Yellow means one replica is unassigned. Yellow has meant that for six months.
"How many shards should I use?" Whatever number you pick, it's wrong.
Elasticsearch is fast. Unless you sort by a field that wasn't mapped as a keyword.
The heap is at 92 percent. The heap has been at 92 percent for a week. The heap will always be at 92 percent.
"We don't need a snapshot policy." Spoken moments before a master node went rogue.
A query took down the cluster. The query was three lines of JSON written by an intern.
"Why is reindexing so slow?" Because it's rewriting half a terabyte one document at a time and you asked for it.
The most expensive word in Elasticsearch is `wildcard`.
"Can you make this search fuzzy?" "Sure. How much do you like timeouts?"
Logstash decided to retry forever. The queue is now larger than the index.
"Just upgrade the major version." There is no `just` in a major Elasticsearch upgrade.
Two kinds of ops engineers: Those who have force-deleted an index in prod, and those about to.
Kibana loads. Kibana loads. Kibana loads. Kibana shows a red banner about a missing field.
"We hit the field-data circuit breaker." Translation: Somebody aggregated on a text field.
Elasticsearch dynamic mapping is helpful the way a stray cat is helpful.
"The cluster is red." Good. At least there's no ambiguity now.
I trust people. I just trust the slow log more.
"Why is disk usage at 95 percent on one node?" Because shard allocation has opinions.
The cluster restarted itself. Nobody asked it to. Nobody is going to bring it up at the standup.
"Can we add a new field to the mapping?" "Sure. Define new."
Elasticsearch index aliases exist so that one day you can pretend the old index never happened.
"The search is returning stale results." Did you refresh the index? Did you bump the refresh interval? Did you forget what refresh interval means again?
A senior engineer is someone who has watched 200 unassigned shards relocate and stayed calm.
"How much does Elastic Cloud cost?" Yes.
The slow query log is the saddest book ever written.
"Our search relevance is bad." Have you tried tuning the analyzer? "What's an analyzer?"
Reindexing on a Friday is a form of self-harm.
"The cluster is green again." For now.
Every team has a dashboard that has been broken since the 6.x to 7.x migration. Nobody touches it.
"Just use a `match_all` query." And that's how OOM happens.
Painless scripting is named optimistically.
"We can ingest a million events per second." We can also drop them silently at the same rate.
ILM is great until it deletes the index the auditor needed.
"Why is the master node electing itself again?" Because two nodes can't see each other and democracy demands a vote.
I asked Elastic support a question. The answer was a link to a 200-page guide.
Cross-cluster search: the feature that lets two broken clusters fail at the same time.
"Why is GC pausing for six seconds?" Because you put a 64 GB heap on the JVM and ignored every Elastic blog post since 2017.
The shard count was inherited from the previous engineer. The previous engineer left in 2019. The shard count is 1000.
"Can we just turn off swap?" You were supposed to turn it off before the cluster started.
Elasticsearch quorum math has cost me more sleep than any romantic relationship.
"It works in the test cluster." The test cluster has three documents.
The dashboard shows everything is fine. The customers are complaining. The dashboard is lying again.
"We need to lower the refresh interval for real-time search." You want indexing to be slower. Got it.
Some people meditate. I watch the recovery API output until the queue drains.
Being on the search team is basically: Making sure the thing nobody thanks you for keeps working at the speed they expect.
WordPress Developer Jokes Jokes
The client said they just needed a simple website. Forty plugins later, we are still defining the word simple.
I installed a new plugin. It conflicts with the other forty plugins, and also with itself.
We picked Elementor. Then we picked Divi for one page. Then we picked Beaver for the footer. The site now loads three page builders before it loads the page.
My theme has a page builder. The page builder has a page builder. Somewhere down there, I think there is still HTML.
The client wants the logo bigger. Just slightly bigger. Forty-seven slight increments later, the logo is the page.
I migrated the site to a new host. The serialized data did not survive the trip.
wp-config.php is the keeper of secrets. It is also the keeper of database credentials I committed to a public repo in 2014.
Did you turn on WP_DEBUG? Did you turn it off before deploy? Nobody answers the second question.
A meta_query took eight seconds. I added an index. It took nine seconds, because now MySQL has to update the index.
Transients are a cache. I do not trust them. They expire when they feel like it, and they persist when I beg them not to.
Yoast SEO took over the editor. I cannot find the publish button. The publish button is now a green dot.
The plugin auto-updater ran at 3 a.m. The site went down at 3:01 a.m. The client noticed at 3:02 a.m.
I found malware. It was in wp-content/uploads/.cache. The hidden directory I never told WordPress to create.
Custom post types are easy. Custom post type archives are not. Custom post type rewrite rules are a small religious experience.
REST API is the new admin-ajax. admin-ajax is the new admin-ajax.
WooCommerce is fast. WooCommerce with one plugin is slower. WooCommerce with the full stack is a story I tell at conferences.
Gutenberg block development is easy. You only need React, webpack, JSX, a build pipeline, and the willingness to redo it next year.
Full Site Editing is the future. The future is taking its time.
I am the developer. I am also the SEO. I am also the support team. I am the entire IT department of a coffee shop in Brooklyn.
The form plugin broke. I have seven backups of the form plugin. The one that works is the one I cannot find.
I deactivated all plugins to debug. The bug went away. The client also went away.
The theme update overwrote my changes. I had child-themed everything except the one file I needed.
Permalinks broke. I flushed the rewrite rules. Permalinks broke differently.
The white screen of death is back. It is no longer white. It is a polite error message that tells you nothing.
I asked for a staging site. The client gave me access to production and asked me to be careful.
There are forty-seven page builders. Each one is the last one I will ever need.
The client wants a slider on the homepage. Six sliders. With autoplay. With music.
I optimized the database. wp_options is now only 200 megabytes. It used to be 800.
Someone left autoload yes on a 4 MB option. Every page load fetched 4 MB. I am not telling you who.
The plugin I depend on was abandoned in 2019. The replacement plugin was abandoned in 2021. The new replacement plugin requires a subscription.
I wrote a hook for save_post. It fires twice. I added a guard. It fires three times. I gave up and added a transient.
The site is fast on my machine. The client uses a phone from 2013 on a hotel Wi-Fi. The site is not fast on the client's machine.
I installed a caching plugin. The caching plugin needed a caching plugin.
The previous developer wrote custom code directly in functions.php of the parent theme. The parent theme just updated.
I tried to update PHP. Three plugins broke. Two plugins were the ones the client paid the most for.
The contact form has been silently failing for two months. The client found out from a customer who emailed them directly to complain.
I added a custom field. It is now in postmeta. There are 1.4 million rows in postmeta. The custom field is one of them.
Multisite seemed like a good idea at the time.
The hosting company has its own caching layer, its own object cache, and its own opinion about my .htaccess file.
The client asked if WordPress is secure. I said yes. The client asked if their site is secure. I said let me get back to you.
I wrote a Gutenberg block. By the time I shipped it, the block API had changed twice and the block editor had a new name.
Every WordPress project starts with a fresh install and ends with a 2 GB folder nobody understands.
wp-content/uploads has 84,000 files. The media library shows 312. The other 83,688 are from a plugin that left in 2019.
The .htaccess file has comments from four previous developers. None of them are me. I am not adding the fifth.
The client's most important plugin was last updated in 2011. It still works. I have stopped asking why.
The theme readme says it has shortcodes. That is not a feature. That is a notice that the site cannot be migrated without the theme.
The plugin says compatible up to WordPress 5.8. The site is on 6.7. The plugin still runs. Nobody is happy about it.
The customizer is deprecated. The site editor is the future. The theme uses neither and renders fine.
Block patterns are the future of content. The client copies and pastes from a Google Doc.
theme.json is 800 lines. The theme renders one color and one font. The other 798 lines are aspirational.
It is 2026. jQuery is still loaded on the front end. I checked. Twice.
I tried to authenticate to the REST API. Cookie auth, application passwords, a JWT plugin, and an OAuth plugin all disagree about how. I used a nonce and looked away.
The client wanted a contact form. The contact form needs a spam plugin, a GDPR plugin, a CAPTCHA plugin, an SMTP plugin, a database logger, a CRM connector, and a notification plugin. The form has one field.
I have explained the difference between WordPress.com and WordPress.org to the same client for the eighth time this year. The ninth time is scheduled for next Tuesday.
There is a custom user role called editor_temp_v2. It was created in 2018 for one freelancer. The freelancer left in 2019. The role has three live users.
wp_options is 2 GB. I ran the optimization plugin. wp_options is now 2.1 GB, because the optimization plugin logs to wp_options.
The post revisions table is 8 GB. The posts table is 40 MB. Every revision of every draft since 2014 is still in there, waiting.
WP-Cron runs on page load. The site has no traffic. The scheduled emails from last March are sitting in a queue, patient.
All the real code lives in mu-plugins. The plugin directory is decorative. The theme is essentially a stylesheet with delusions.
There is a must-use plugin that fixes a plugin that breaks every other plugin. Deactivating the must-use plugin is not on the table.
See also
- 70 JavaScript Jokes Every JS Developer Has Lived
- 55 PHP Developer Jokes Only PHP Devs Truly Get
- 55 Back-End Developer Jokes Every Backend Dev Will Get
- 65 Senior Developer Jokes Only Senior Engineers Will Get
- 50 Junior Developer Jokes Every Junior Has Lived
- 60 Stack Overflow Jokes for Every Developer Who Has Copy-Pasted an Answer
- 50 Regex Jokes for People Who Now Have Two Problems: the
reimport that turns a one-line script into a two-day project. - 55 API Integration Jokes for People Reading Someone Else's Docs: the
requests.get()that returns 200 OK with a body that says ERROR.
Sources
Authoritative references this article was fact-checked against.
- Python 3 documentationdocs.python.org
- Python Enhancement Proposalspeps.python.org





