Until now I’ve been using CC more in the vibe coding flow. It was for internal tools where I really didn’t care as much about the overall code quality or whether it fit my particular style. Occasionally I’d have to help untangle some issues if things went really south (like putting some piece of state client side instead of server side or vice versa, etc.)
Then I tried to use it for the main project that I’m working on. It’s not a huge project, not small either. Maybe 300-400K LOC total across all the various components. I was able to pretty consistently get the functionality to work but once I went to review the code it looked like a pretty horrible spaghetti. Even despite a pretty clean starting point and good agentic docs. Sometimes refactoring the outcome and getting things in a merge ready shape would take 3x longer than getting the functionality to work in the first place.
On top of that as everyone knows CC just loves to yap. Even when it’s brief it’s still a wall of text, just shorter.
Following an almost passing remark from this talk:
https://youtu.be/Ib5GBkD555M?t=978
about communicating with LLMs more using pseudo code and diagrams I decided to try it out. And I think it really works pretty well. Here’s roughly what I now have in my .md workflow docs that describe to the LLM how to communicate what it wants to change:
1. Files. New and affected, in a tree. MyProjectRoot/ src/ SomeNewFolder/ (new) SomeNewFile.h (new) SomeNewFile.cpp (new) SomeExistingFolder/ SomeExistingAffectedFile.h SomeExistingAffectedFile.cpp SomeOtherNewFile.h (new) SomeOtherNewFile.cpp (new) 2. API and important state. What each class is responsible for, and what it holds. cpp (new) class SomeNewClass : SomeExistingClass { public: void SomeMethod(int x, int y); /// Worker thread, not the main UI thread. void SomeMethodForOtherThreads(); private: std::reference_wrapper<SomeExistingOtherClass> _otherClass; SomeData _someSignificantState; std::mutex _pendingMutex; // guards _pending only int _pending = 0; }; class SomeExistingClass : (new) NowDerivesFromThis { public: (new) void NowHasThisMethod(int x, int y); void SomeExistingMethod(int x, int y, (new) float nowTakesThis); void SomeOtherMethod(int abc, (was: int) float changedType, (deleted) int unusedArg); /// Now thread safe - comment for non-sig changes void SignatureUnchanged(); private: (deleted) float _gone; (new) int _added = 0; (was: SomeClass1&) std::reference_wrapper<SomeClass2> _changedType; (new) std::mutex _mutex; // guards _nowSynchronized uint64_t _nowSynchronized = 0; }; 3. Key methods, as pseudo-code. Only the ones where the relationships are the point. cpp void SomeExistingClass::SomeExistingMethod(int x, int y, (new) NewStateData state) { ... (new) NowCallsThis::OnThisClass(x, y, state); (was: x * x + y * y) auto z = NowAUtility::ThatComputesIt(x, y); (deleted) // OldCode::NoLongerCalled(x, y); for (auto i = 0; i < x * y; i++) { _something.InALoop(i); } // Summarise anything long, but never cryptically: the intent has to survive. }
I’ve been using this for a couple of days I must say it’s way more pleasant. I can tell at a glance what’s about to happen and I don’t have to read a wall of text just to have an idea of where things land. And once the feature is completed I know more or less where everything is and I have to make way fewer corrections.
Just thought I’d share. Perhaps others have variations on this approach.
submitted by
/u/julkopki
Originally posted by u/julkopki on r/ClaudeCode
