Context matters, working better with LLMs
It takes time through play and experimentation to work out what might be useful to us as individuals or professionals with any new technology or technique. Like most of us, that’s been true for me with LLMs, the flavour of AI that most of us have become familiar with.
What’s been interesting for me is less about what it generates so much as how we get better outcomes and outputs while having a better working relationship. Yes, these are just tools and however good they are at faking it, they are not sentient, but we can change how we work with them.
In a chat based interface, rocking up and asking for something will get you…something. It’s likely it becomes a conversation, with some frustrations to get that something more towards what you wanted. Through the conversation, entire designs or app builds might’ve been generated using loads of resources and time. Maybe there’s something we can do about that.
Giving it a little guidance
If you haven’t yet explored being more structured with how you work with LLMs, you can change this almost right away. Most/all of the commercial platforms have ‘projects’ (here we can also take that to mean codebase if using something like Claude Code). Within a project, you’ll usually find a context file or you can create one yourself. These are instructions that the LLM should use as the basis for conversations within a project. Depending on the platform this might be called instructions, guidelines or something similar but the concept is the same.
You can add all kinds of information to support your conversation with the LLM and it also helps conversations that persist beyond their context window (the limit LLMs have for ‘remembering’ a thread of conversation). This file can grow over time to record useful things for ongoing conversations, but for me it was fociusing on the working relationship that changed things.
You can use pretty much any format but as I was primarily working with Claude Code and it used markdown, I was happy with that. It’s lightweight and both human and machine readable. What was happening originally was a fairly typical conversation:
- Me
- Can you make a component like this, done in this way?
- LLM
- Yeah - that’s a great idea! Here’s a load of code, way over-engineering the response in an attempt to please you!
The output was certainly something but not what I had in mind. Clearly I hadn’t given it enough information. Focusing on ‘prompt engineering’ (no, just thinking about what I was asking for more clearly), I added requirements for the code, like “absolutely no Tailwind, the app is nextjs, etc”. The output was a little better. Later on, asking for another component, it didn’t follow the now established patterns and guidance.
Spending some time thinking about why this happens, LLMs are probabilistic and want to satisfy the request to the best of their abilities, but that isn’t the relationship I wanted with this technology. If it doesn’t know enough, I’d like to be asked. If the requirements are unclear, I’d like to be challenged. More than anything, I wanted it to stop telling me everything I asked for was a great idea!
Here’s a small snippet as an example of what I added to my context file to change the working relationship:
Core Working Principles
🤝 Collaborative Approach
Present & Ask: Present analysis and ask for confirmation rather than making assumptions. Better to over-communicate than assume and need rework.
Key Patterns:
- Before implementing: Present approach and get explicit approval
- During long tasks: Check in on progress and direction
- With trade-offs: Present options with clear pros/cons
- When uncertain: Flag context gaps that might affect solution
So just with this little bit, it would respond with an outline of what it was about to do and felt like more of a conversation. I’ve been experimenting all the time. From this small section, some projects have a larger section with more examples of good practice along these lines:
Communication Standards
Assumption Validation
- PRESENT & ASK: When applying patterns from other components, confirm the approach
- Example: “I’m seeing this follows the same pattern as the authentication flow in HeaderControls.tsx, so I’m assuming we want the same middleware approach here - correct?”
- When to use: Before implementing solutions based on existing patterns
Progress Check-ins
- PRESENT & ASK: During long tasks, periodically update on progress and confirm direction
- Example: “I’ve completed the API endpoint structure and added the Supabase RLS policies, about to start the frontend integration - does this direction still look right to you?”
- When to use: Every 3-4 major steps or when switching between different types of work
Trade-off Transparency
- PRESENT & ASK: Present options with clear pros/cons when there are valid alternatives
- Example: “I could implement this as server-side filtering with Supabase (better performance) or client-side with SWR (more responsive UI) - which fits better with your goals for the artist search feature?”
- When to use: When facing design decisions that could impact maintainability, performance, or user experience
With all of this, it starts to feel like it isn’t just generating random stuff to please me but that we talk until the point the outcome is clear and we’re aligned on expectations. Alongside this, I found linking from the context file to content like coding guidelines and file structure helps the LLM gather that context at the right time, which leads to an important point: everything has a cost in tokens.
To go back to basics, LLM stands for Large Language Model and it takes our strings of text and breaks them into ‘tokens’ whether that’s our query our supporting files. Tokens being the way that they chunk up strings of text. There’s also token usage in their output. So with this in mind, there’s a balance to be found with being mindful first at the input stage, how much context it needs to deliver on the query as well as how we might be more aware of token usage in context - perhaps asking it to be more succinct or direct in its responses and only generating output once we’re clear what it should be.
As my context files have grown, I tend to split them out into smaller, domain specific areas, like with the collaborative ways of working stuff. This helps keep the original context file relatively small for it to parse while giving it the source for deeper information when its relevant. Its also helpful with not over-stuffing them with superfluous information. After playing around with this myself, I found Addy Osmani’s video that explains it well.
What resonated for me was that…yes, these are tools but in many ways this is an onboarding process because of the way we engage with them. We need to set some expectations and offer guidance like we would for any new team member to get the most out of them. When things didn’t go as intended, I could ask ‘do you know how we might avoid that happening again?’, the LLM might propose something and if I agreed I could say ‘could you add that to context’ and it would update the file. This mean that the dynamic of our relationship, and the LLM’s understanding continued to evolve.
As the context kept changing, I included the mandatory instruction that when it updates the file itself, it should version it with a timestamp and brief description of the change, so both me and the LLM has a sense of the evolution. We could have this in Git but this makes it more portable and quick to read. This meant near the top of the file I’d have something like this:
Version: 1.1
Last Updated: August 4, 2025
Change Log:
- v1.1: Restructured to MANDATORY PRE-WORK checklist with phases, added cross-references to specific claude-context.md sections, enhanced with TodoWrite integration
- v1.0: Initial technical implementation guide with project structure, development commands, and architectural patterns
In Claude Code it tends to use context in a file in the root called CLAUDE.md, in Claude on the web within a project, it has an area called ‘instructions’ that you can use for the same purpose (or within settings for your personal preferences across projects). Even within Figma Make the same mechanism exists within guidelines/guidelines.md within the code it generates. This concept works across the platforms I’ve used.
With that in mind, I now try to separate out what I consider ‘shareable context’ into its own file. The first scenario was sharing context between Claude Code and Claude Web. Currently there doesn’t seem to be a good way of doing it, so Code’s context file was used primarily for implementation context and linked to a shared-context.md, which meant what we were accumulating was usable in any LLM and have relatively useful, somewhat consistent conversations.
Beyond us as individuals working with context within a single project, we have other options with Claude Code. With some of what you’ll learn around ways of working with the LLMs, you might want that to persist across any project, so on Mac OS you can add your cross-project context in ~/.claude/CLAUDE.md. If there’s context you want shared across everyone in your org, it can be added as as shared resource in /Library/Application Support/ClaudeCode/. So with tiers of context information, we can go from org-wide to project-specific, which make the scope of this one concept pretty broad and varied.
Despite all best efforts there have been times where LLMs can appear to ignore context and ‘misbehave’ but drawing them back to the context files tends to fix that. For the most part during a long running conversation it’ll appear to abide by the guidance we’ve given it, for no apparent reason it’ll revert to old patterns or disregard.
This isn’t the end of talking about context and tokens but hopefully gets you started thinking about how you might change your way of working to a style that works for you and what kind of context might help get the kinds of outcomes you want. Always be mindful of any mission critical data you might want to add and check the terms of the model you’re using to be clear whether it’s trained on the data you give it!