Stop Using Dark Magic: Reflection

You sold your soul to get this crud app out faster, great

Michael Yoniles

devsoftware

1967  ⏲ Reading Time:8 Minutes, 56 Seconds

2026-09-15 13:47 -0500


Mirror Mirror on the wall

I have sometimes compared writing code to being a wizard. You speak in arcane runes, and read even more arcane spellbooks (documentation) to create systems that appear as magic to a lay person. In this analogy, the masters of the past have already sealed away the crazy, wild magic of old into tools both to make development easier but also to avoid the horrific ramifications of wizard apprentices who cannot be trusted with the full power of what they play with.

And you, the apprentice that you are, throw away these safeguards because you somehow fucked up writing a CRUD app. You looked at the decades of compiler development and programming convention and said “Fuck it, I will use dark magic to cheat the compiler and implement my obviously bad design” because you didn’t think it would actually reach production. This is a personal slight against me, not as somebody who cares about clean code, but as someone who knows that they too, are a dumbass.

There is nothing that makes me fear for my life more than seeing Class.ForName or peeking behind the curtain to look at python objects’ __dict__s (pause) using strange incantations. A reasonable engineer may tell you that there is nothing to fear; that reflection has its place within the programming ecosystem as a tool for Dependency Injection or ORMs. They are right, in the hands of talented library authors, reflection can do amazing things. But you are not a library author. Your place within the programming ecosystem is being the first person that I feed to the wolves because I had to read a stacktrace from a NullPointerException in a dynamically-invoked method on an auto-generated proxy. There is no God in that stacktrace and only he could save you from my retribution.

I Will Fucking Banish You To Ethereal Plane If You Suggest Reflection

I get it. Its really easy to box yourself in using OOP. There’s an internal private variable set four levels down in a class hierarchy, or some private method that can only be tested with seventy lines of Mockito and an Adderall prescription. Maybe you never learned what an interface was, or simply think that reflection is the fewest lines of code needed to implement a registry. If you think, however, that the best way to get wanted behavior at runtime or even to test some function is to tear down access control and make the computer re-read your shitty source code then I am hurling you into the sun. Just because you skipped your CompSci 101 classes on interfaces, there is now a production system that, at runtime, has to figure out if any random class can foo(). And God help you if you overwrite a private variable based on user-input. You better hope that your address is private or I will hunt you down and personally input you six feet into the ground.

The absolute fucking wizards of engineers who came before you wrote compilers for themselves and others to stop themselves from blowing up their own foot but you, in your hubris, ignore these rules and conventions. The wizard council of four encoded common programming patterns into a tome that everyone pretends to have read but even with that knowledge, you could not cobble together a working app without spitting in the face of good design. Do you really think that your CRUD app is so novel that it cannot possibly be solved by code that a compiler can check? Are you arrogant enough to believe that your internal tool with twenty users needs to get out fast enough that you need to implement adhoc duck-typing?

I am sure that you feel like you are writing reuseable, big-brain code but this is an illusion. The moment some jagoff raises a nit three months down the line to rename a class’s variable from firstname to firstName, all of that falls apart. Instead of the compiler catching that issue in three seconds, prod will start serializing null into a database three microservices away. Even if I am that jagoff, you better hope that the compiler catches this mistake before I catch you and re-enact the Stone-Cold Stunner.

You could have written an app where stacktraces make sense. You could have written an app that doesn’t blow up in production because you put a file in the wrong folder. But instead you invoked the dark magic of byte-code fuckery because a LinkedIn warlock convinced you that it is what 10x, next level engineers do. And for what? What could possibly be worth selling your soul to the meta-programming demons? I’m not going to wait for you to answer that because it can’t possibly be worth the horrors that I will unleash on you in righteous retribution. There is a performance overhead. Maintaining the code becomes downright impossible. And all those fancy acronyms like SOLID and DRY that engineers pretend are important get sacrificed at the altar of your job security. I can only pray that you are the engineer paged at 4AM to discover that the class ConcreteFactoryBuilderFacade doesn’t have the field enterprise causing the reflective getattr on that variable to fail. Maybe then, in your half-asleep state, would you have the humility to ask yourself why you didn’t implement a getter like every other goddamn developer since 1974. Maybe then also, in your half-asleep, state would you also not be able to hear me rushing towards you at mach-fuck to piledrive you into dust.

Oh But Libraries Like ORMS And Most Frameworks Use Reflec-

YOU ARE NOT THAT GUY PAL.

Are you writing an ORM? Are you writing a framework? Actually, don’t answer that second one. This goes back to what I said earlier: have some goddamn humility or I will strike you down with holy fury. You are probably writing a data-processing app or god forbid you are using reflection in a web app. At no point in the boar book did the authors write a chapter on ignoring the existence of interfaces nor could they possibly know that I would invoke ancient magic to seal you into the shadow realm for using reflection.

Let’s say that in your delusion you think that you are that guy. I promise you that you aren’t but lets say that you think you are. Are your coworkers now meant to maintain your bespoke, domain-specific (maybe even app-specific) framework until the end of time? You are subjecting them to endless torment and agony every time they have to work with your custom ORM just because you didn’t want to import something an actual library author wrote or implement a proper Repository.

The developers of Spring/Django/etc earned the right to use reflection; they are great wizards. You decided that you did not want to write a switch statement and now nobody in the codebase can search for who calls foo(). Nobody calls foo(). Its invoked by saying its true name (like a demon) that is defined in a yaml file written by a guy who left the company in 2019. God help you when the intern deletes the ‘dead code’ that is currently processing payments in production. Spring has a test suite the size of a small nation’s GDP and more contributors than Bonnie Blue. You saw a LinkedIn post and are going to have an LLM generate the unit tests for you.

I Only Use It For Testi-

The only thing you are testing is my patience before I terminate your bloodline. If you are telling me that there is no way that you can access a field or variable then you are just admitting that you are bad at programming and deserve whatever I do in self-defense. This is self defense of course because you have already destined violence upon me. When I inevitably get the ticket to refactor your slop into something that doesn’t have runtime errors, those tests will fail. Not because I screwed up the business logic or anything important, but because my setter is being overwritten by some unknown, unfindable class or setup function overwriting a private variable.

What was meant to be testing interfaces became impossible because that interface only exists in your head. Nowhere is it written that all MyTypes have to have a function called bar(), its just a happy coincidence that they all do. The only way that I could possibly know that SuperSecureClass secretly had a setter on secretString is if I compiled and ran the code in my head or asked the author themselves.

I know what you are going to say. That this class simply is not easy to test, that writing ReflectionTestUtils.setField(instance, "myPrivateField", "newValue"); is easier than writing a setter. This is correct but also insane. You have decided that the one place where code is supposed to be a clean, honest description of the programs interface is exactly where we need to break encapsulation, access modifiers, and every single intention of the author. What you actually should be saying (and probably thought) is that this class is dogwater, doing too much, or doesn’t use DI. And instead of restructuring the app for you 1 point jira ticket, you decide to grab its organs directly so you can get your one required test case to go green. I really do empathize with you as I dropback suplex you into the concrete below but that is no excuse to invoke dark magicks.

Its Really Not Hard To Keep Track of References, AI Is Really Good At Hunting Those Dow-

Wow what a great idea! Let’s hand artisanally crafted bad design to the ‘Mathematically-Average Code Machine’. Let’s walk through what you are actually proposing. We can write code in such a way that static analysis cannot find callers, references, etc to the point where IDEs and LSPs (software worked on by hundreds of engineers designed solely for the purpose of finding where things are referenced) cannot tell you what references what. BUT this is all ok because we have a non-deterministic text-generator that may or may not know that User needs to quack like a duck based on its context, model, and random bits. You are willfully and gleefully admiting both that no human could possibly keep track of all of this and that you fully deserve the Mortal Kombat fatality that I will perform when I reach you.

If you are ready and willing to double the technical and cognitive debt of the project, let AI hunt down and expand the reflective programming patterns. Its only saving grace is that LLMs are great at producing files to replace and maybe salvage this sinking ship of an app. If you use it for any other purpose then you must accept the fact that you used dark magic and are now calling upon disparate voices from the void to not lose control of it.

What can I do instead?

Write the gosh-darn boilerplate or hell even have an LLM generate it for you. Write a constructor that takes its dependencies like a real wizard instead of summoning them from the shadow realm. If your class can’t be tested or a function can’t be invoked without prying it open, you have a design problem and relection is just a bandaid to avoid having to deal with it. Not only will you sleep better at night with more traditional programming techniques but both you and your teammates (human and software) will be able to understand what the program is doing.