When Architecture Refuses to Be Understood
Introduction: The Promise of Clarity
Every engineer eventually meets a system that resists being understood. Not because itâs complex in an elegant way, but because itâs complex in a historical way â a sedimentary stack of decisions, rewrites, patches, and compromises that no longer resemble a coherent architecture.
For me, that system was ConnectWise Manage.
What began as a straightforward goal â reproduce the browserâs behavior so I could automate repetitive work â turned into a monthsâlong excavation of a legacy product wrapped in modern UI paint. And somewhere in that excavation, I hit a moment that forced me to confront a deeper truth about architecture, automation, and the emotional cost of fighting systems that donât want to be automated.
This is the story of that moment.
1. The Goal: Reproduce the Browser
The initial objective was simple:
Build a deterministic, scriptable client that behaves exactly like a real browser.
Not âclose enough.â
Not âgood enough for internal use.â
Exact.
Because ConnectWise doesnât expose a modern UI API, the only way to automate workflows was to replicate the browserâs behavior down to the smallest detail.
This meant:
- capturing every request
- preserving every cookie
- matching every redirect
- replaying every header
- honoring every hidden field
- mimicking every JavaScriptâgenerated value
A browser isnât just a request generator.
Itâs a choreography engine.
And I needed to reproduce the dance.
2. The Authentication Flow (The Part That Actually Worked)
Hereâs the twist most people donât expect:
The authentication flow was the one part that behaved like a modern system.
It was built on Ruby on Rails â predictable, structured, and consistent. Once I mapped the sequence:
- initial GET
- hidden fields
- antiâforgery tokens
- JavaScript mutations
- POST
- redirect chain
- session cookies
âŠI was able to reproduce it perfectly in Python.
Authentication wasnât the problem.
Authentication was the victory.
The collapse came later.
3. The Real Failure: UI Hydration
Once authenticated, the browser loads the ConnectWise SPA (Single Page Application).
This SPA generates a set of UI context objects that the backend expects on every UI API call.
These include:
- board context
- ticket context
- view state
- screen ID
- metadata caches
- user parameters
- internal navigation state
These values:
- are created in JavaScript
- live only in memory
- mutate as the user navigates
- are never exposed through a public API
- are required for every ticketârelated endpoint
Without them, the backend cannot resolve:
- which board youâre on
- which filters are applied
- which columns are visible
- which ticket context is active
- which UI state the user is âinâ
This is the architectural truth:
The UI API is not a data API. It is a UI state machine.
And state machines cannot be automated without participating in the state transitions.
4. What Happens When You Call UI Endpoints Without UI State
4.1 Ticket details
getticket.rails
â 500 Internal Server Error
Not because the request is wrong â
but because the backend dereferences null UI objects.
4.2 Ticket notes
getticketnotes.rails
â 500 or empty payload
Because the ticket context was never hydrated.
4.3 Multiline board data
GetMultilineDataAction_srboard.rails
â returns 0 rows
Not because you have no tickets â
but because the backend cannot determine which board to query.
Your session was valid.
Your cookies were correct.
Your authentication was perfect.
But the UI API refused to speak to you because you werenât a browser.
This was the collapse.
5. Why I Donât Use Selenium (Even Though It Would Have Worked)
Whenever you talk about reverseâengineering a browser flow, someone inevitably asks:
âWhy didnât you just use Selenium?â
And hereâs the thing:
Selenium absolutely would have worked.
It would have executed the JavaScript, hydrated the UI state, and satisfied the backendâs expectations.
But thatâs exactly why I didnât use it.
Selenium solves the surface problem while violating the architectural goal.
1. Selenium gives you a session, not understanding
My goal wasnât âlog in.â
My goal was:
- understand the flow
- map the architecture
- build a deterministic client
- eliminate the browser entirely
Selenium gives you none of that.
It gives you a robot that drives Chrome.
2. Selenium is not automation â itâs delegation
If the browser is still required, the system isnât automated â itâs puppeteered.
3. Selenium is fragile in all the wrong ways
It introduces:
- timing variance
- DOM dependency
- rendering delays
- race conditions
- headless quirks
It works until it doesnât.
4. Selenium is incompatible with operatorâgrade tooling
A real automation client must be:
- portable
- scriptable
- testable
- versionâcontrolled
- environmentâagnostic
- reproducible
Selenium is none of these.
5. Selenium would have defeated the purpose
Yes, it would have hydrated the UI state.
But it would have:
- hidden the architecture
- masked the inconsistencies
- tied the workflow to a browser
- created a brittle dependency
- undermined the entire point of the project
The goal wasnât to âget in.â
The goal was to understand why automation fails.
Selenium would have given me a session at the cost of the truth.
And the truth matters more.
6. Why I Wanted to Automate It in the First Place
A big part of this project wasnât just about logging in.
It was about simplifying a UI that was never designed to be simple.
I had already proven this was possible with Acronis.
Acronisâ UI is clunky â painfully so â but underneath the clutter is a modern UI API. Once you peel back the layers, the architecture is clean enough to automate. You can build a CLI thatâs faster, clearer, and more humane than the portal itself.
And I did.
I deâclunkified Acronis because the architecture allowed it.
ConnectWise is the opposite.
ConnectWise is built in a world where the server is too nosy
Modern systems treat the client as the source of truth:
- the client requests data
- the server returns data
ConnectWise was built in a different era â one where:
- the server tracks UI state
- the server tracks which screen youâre on
- the server tracks which filters youâve applied
- the server expects the client to simulate a WebForms page lifecycle
Itâs architecture from a time when the server believed it owned the UI.
Acronis: âTell me what you want.â
ConnectWise: âTell me what screen youâre on.â
Thatâs the difference.
This is why the UI hydration failure mattered so much.
It wasnât just about fetching tickets â
it was about unlocking the ability to build a better interface on top of a system that desperately needs one.
And when the architecture itself refused to cooperate, it wasnât just a technical failure.
It was the collapse of the entire premise.
7. The Collapse
When the final test failed â silently, inexplicably, architecturally â something in me gave out.
Not anger.
Not frustration.
Just collapse.
I cried harder than I expected.
Not because of the endpoint.
Not because of the payload.
Because of the meaning behind it:
- the hours invested
- the clarity I thought Iâd earned
- the relief I thought was coming
- the automation that would have protected me
- the realization that the system was designed to resist me
And after the crying came the shutdown â the kind of sleep that isnât chosen. The kind where your body says, âWeâre done here.â
8. The Drift Toward Determinism
When I woke up, the first thing my mind reached for wasnât rest.
It was the ffmpeg transcoding flow.
Not because I needed it.
Because it made sense.
Because it was deterministic.
Because it obeyed rules.
Because it didnât fight me.
Because it didnât require emotional labor.
Because it was a system where architecture behaves like architecture.
Thatâs the essence of Empirical Drift:
When one system collapses under architectural inconsistency, I drift toward one that rewards clarity.
9. The Lesson
Hereâs the architectural truth at the center of all this:
You cannot automate your way out of an architecture that was designed to resist automation.
Some systems are not automatable.
Some workflows cannot be made deterministic.
Some platforms require human presence because they were built that way.
Some architectures punish clarity instead of rewarding it.
And the human cost of fighting those systems is real.
Conclusion: The Cost of Clarity
This wasnât a story about burnout.
It was a story about architecture â and the emotional physics of working inside systems that refuse to be understood.
The ConnectWise UI API didnât just fail technically.
It failed philosophically.
And in that failure, it revealed something important:
- why clarity matters
- why determinism matters
- why architecture matters
- why engineers drift toward systems that behave predictably
- why the cost of inconsistency is paid in human exhaustion
Empirical Drift isnât about feelings for their own sake.
Itâs about the way technical systems shape the people who operate them.
And sometimes, the hardest truth is the simplest one:
Some systems canât be fixed â only understood. And sometimes, understanding is the end of the road.
Appendix: Why Ticket Retrieval Fails Without a Hydrated UI Session
This appendix documents the actual architectural failure point:
ConnectWiseâs UI endpoints require a fully hydrated UI session that only the SPA can produce.
The authentication flow itself â the Rails portion â was one of the only parts that behaved predictably. The collapse happened after login, when attempting to retrieve ticket data without the UI state that the SPA normally generates.
1. Authentication Did Work (Because Rails Is Predictable)
The Rails-based login flow:
- accepted your credentials
- issued the expected cookies
- completed the redirect chain
- produced a valid authenticated browser session
Your Python client reproduced this flow perfectly.
This part was not the problem.
The problem was everything that came after.
2. The UI API Requires SPAâGenerated State
Once authenticated, the browser loads the ConnectWise SPA.
This SPA generates a set of UI context objects that the backend expects on every UI API call.
These include:
cwClientIdcwViewStatecwScreenIdcwContextIdcwBoardContextcwTicketContextcwUserParamscwMetadataCache
These values are:
- created in JavaScript
- mutated as the user navigates
- stored in memory, not cookies
- never exposed through a public API
- required for all ticketârelated endpoints
Without these, the backend cannot resolve:
- which board youâre on
- which filters are applied
- which columns are visible
- which ticket context is active
- which UI state the user is âinâ
This is the architectural truth:
The UI API is not a data API. It is a UI state machine.
And state machines cannot be automated without participating in the state transitions.
3. What Happens When You Call UI Endpoints Without UI State
3.1 getticket.rails
â 500 Internal Server Error
Because the backend dereferences null UI objects.
3.2 ticketemail.rails
â 500 or empty payload
Because the ticket context was never hydrated.
3.3 getticketnotes.rails
â 500
Same reason â missing UI state.
3.4 Multiline endpoints (GetMultilineDataAction_srboard.rails)
â 0 rows
Not because you have no tickets â
but because the backend cannot determine which board to query.
Your session was valid.
Your cookies were correct.
Your authentication was perfect.
But the UI API refused to speak to you because you werenât a browser.
4. Why This Cannot Be Reproduced Programmatically
To hydrate UI state, you would need to:
- execute the SPAâs JavaScript
- maintain its internal memory objects
- simulate navigation events
- reproduce the WebForms lifecycle
- track board and ticket context
- maintain the same timing and ordering as the browser
At that point, youâre not automating ConnectWise.
Youâre reâimplementing ConnectWise.
And thatâs the line where automation stops being automation and becomes emulation.
5. The Final Takeaway
The authentication flow was solvable.
The UI API was not.
Because the UI API is not an API â
it is a reflection of UI state, not a data interface.
And without that state, the backend collapses.
This is why the project ended where it did.
Not because you failed,
but because the architecture itself is incompatible with automation.