Mon 16 March 2026
Learning from Sales
It is not the job of sales to find customers with problems that we have solved, but also to find the problems that we can potentially solve. The role is one part customer relations and one part business development.
The challenge with this role is that features fall on a spectrum from nice-to-have to critical-for-business; being able to tell the difference is a key skill for anyone in sales. If we filter all the nice-to-have functionality out of the developer's backlog what remains is the high value and impactful functions that allow us to sign more deals.
Engineering teams often miss this business context and lack the skill to differentiate these features. Part of the problem stems from being a few steps removed from the customer and nice-to-have features can appear more exciting to work on but don't end up creating value.
Engineers that wish to be highly autonomous and are charged with seeking impactful work need to exercise their inner salesperson. Those that don't embrace these skills are at risk of wasting their time and working on the wrong thing.
Buy Signals
Those in sales and business dev look for hints that inform them of the project's potential to get to the deal signing stage. Focusing on projects with high potential allows us to avoid chasing cases that won't move forward or are likely to end without being closed.
If more than one customer is asking for a feature this can be an obvious indication that focusing on this can provide more impact. If you're working on internal software infrastructure, are you getting a feature request from more than one team, or is it from the esoteric data scientist that seems to have their own bespoke setup?
Sales rely on buy signals. A buy signal can indicate when a client is excited enough by a solution that they are willing to consider a purchase. The most obvious way they signal this is by giving you money. If they're trying to give you money before the solution is built, you'll know the solution is somewhat important to them.
We don't have to rely on the client putting down money for us to recognise a buy signal. Buy signals can appear from any skin a customer is willing to risk for a solution. If a customer is willing to vouch for the product to a superior this is a political stake. This allows us to move up the decision chain and provides a positive signal that a deal can make it to close. We also use this as a way of making progress in conversations without explicitly asking for money.
Software engineers should want to get closer to the projects that have the largest impact. We can use buy signals to determine the projects with the most buy-in from leadership and create the most business.
Discovery Questions
Developers can find themselves working on the right project but can end up developing the wrong thing without an accurate idea of the pain-point. We need to peel back the layers that surround a customer's pain to determine how we may best deliver.
Sales do this with discovery sessions or hearing it directly from the customer. Their goal is to avoid making incorrect assumptions about the problem which may lead to building the wrong solution. Software engineers could also benefit from this skill. If you're given a spec an engineer should ensure they're delivering the appropriate fix, otherwise they risk missing the target and have to do revisions or start again from scratch.
Sales does this with open ended questions, the more space they provide a customer to explore the problem the higher the chance we expose insight. Getting them to dig deeper on specific points can expose issues they might be having and asking how a solution might affect their business can help sales qualify this lead.
A classic technique of getting your customer to talk is called Mirroring covered in "Never Split the Difference" by Chris Voss.
Are your developers asking enough questions to expose misaligned assumptions?
Touch Points
Progress updates with customers allow you to show that you are keeping them in mind and their requests haven't been forgotten about. They also provide a heads up when new features are near release. They are used to build trust and allow the customer to feel like they are helping guide the process.
If your wins are also their wins they will feel like they have more skin in the project's success and become an internal champion across the industry and in their own business.
How often do you provide a task to a developer and only hear about it three months later when there's just two weeks until the deadline?
Regular touch points don't just serve the relationship you are building with your customers but also allow you to validate the project closer to real-time. Ensuring that you are headed in the correct direction is critical for success. If you're heading down the wrong path you'll want to know as soon as possible.
It's a shame that developers seem to be stereotyped as introverts lacking people skills that wish to be isolated from any call with the customer. The industry appears to lean into this idea by adding more barriers between the client and developer. The truth is the companies that have their developers closest to users and customers are the ones that succeed and the best way for an engineer to level up is to care more about the customer.
Determine Timelines
Software can rely on external processes and teams. Figure out what you're building and predict where you might need approvals or contracts, this will allow you to get the ball rolling in those departments. Sales already do this on their discovery calls by probing if the customer will need to bump other departments, bumping them now can lead to less delay.
Within a company we might require a third party tool for the new feature we're developing, so the sooner we loop in finance or the security team the better. We can have them work on their specifics in the background while we work on the feature. We can probe our PM before we start the project that we might require looping in these other teams.
We should also determine when something will be needed and understand the timeline. They could be aiming to deploy their MVP in a month which could affect how we prioritise building the solution. If the deadline is tight, perhaps we can determine if there's only a subset of features required for the MVP and avoid sinking time into the features that are expected at a later time.
Alignment
Developers can level themselves up by becoming more aligned with the business and not less. One way of doing this is by moving closer to the customer or learning how other roles operate. Software will continue to be a competitive industry and engineers can't afford to waste time on things that don't delight their users and/or provide impact.
Mon 09 March 2026
The Moderate Take
Economics and politics are determined by the most compelling stories and occasionally it is hit by reality. This is one of the challenges when following reactionary discourses on platforms like Linkedin, Reddit and Twitter.
When advice comes from board members and VCs I am reminded that they scroll the same threads on reddit that I do. Their opinion is often shaped by the highest voted comment in these forums.
Unfortunately we aren't gripped by stories that are filled with the context and the caveats that exist in the real world and this has shifted us into more extreme political leanings. Why should we fill our content with details when this jeopardizes the opportunity of going viral.

False narratives get the clicks and impact stocks because they're entertaining and persuading. We are willing to sit still and listen to the stories that are novel and gripping. 14 years ago Elon said we would land people on Mars in 10 years. (15-20 years in the worst-case).
including caveats all the time makes articles too awkward to read and buries your actual point
No one finds being moderate sexy.
The boring parts of software
The engineers that have the largest impact are the ones that read the most documentation. Getting through software specs is tough, they're not filled with fluffy prose and they're often dense and technical, but nothing delivers value like saying: "We don't need to do that work, that feature already exists in the API".
Similarly, the largest mistakes I've seen made in software come from conclusions being made too quickly after reading a single page of documentation. Engineers also avoid reading documentation by jumping on a band wagon of agreement without verification.
This extends to how reactionary takes tend to mold their opinions. Life is easier when someone does the work for you. If things go belly up, hey that wasn't my misinterpretation.
Where's it shifting?
This doesn't just apply to engineer's, Steve Eisman had this to say about the finance industry in 2008, but this applies to everyone.
I think one of the hardest things for all human beings, me too, to deal with our paradigm shifts. You know, you exist in a paradigm. It's been around from a very, very long time. Your whole career is based on that paradigm. you've made a lot of money in that paradigm and then it turns out that the paradigm is either changing because of technology or maybe the paradigm was actually wrong because it was based on continuously increasing leverage which is what the financial services industry's paradigm was based on human beings have tremendously difficult time dealing with paradigm shifts. Tremendous.
It's like a nightmare. They don't want to deal with it.
- Steve Eisman A.k.a Steve Carell in The Big Short
r/ExperiencedDevs is an echo chamber degrading Ai slop and crafting existential dread in the industry. They assert that Ai can never be as detailed or accurate as they are when it comes to writing code, at the same time they're shaking in their boots about the future of their industry. When there's commentary on the benefits of AI these comments tend to be downvoted or deleted and the blame is put on automated bots making up pro-ai agitprop.
Software engineers tend to be defensive when it comes to generative code and state that most of their job wasn't code to begin with.1 These engineers aren't out of the "Ai makes mistakes" phase or they're moving goal post to take the target off their back.
The latest cohort of uni students are winning hack-a-thons without knowing how their applications works. r/ExperiencedDevs needs to come to terms with the unknown and as long as this industry has been around we've never actually known how projects work in their entirety. We've never read every line of code in our dependencies, not knowing how something works and being able to make contributions is not a new phenomenon. Those that have succeeded in this industry can wade through the unknown and that will continue to be the case.
It's normal to have strong feelings about the grads picking something up in a short amount of time that may have taken you longer to hone in on. It's the classic "back in my day" reaction.
Extremes
Unfortunately avoiding nuance leads us to extremes.
The truth is; software engineering looks different and requires different skills at different stages of the business and stack. These complaints might just be a misalignment with the business.
Ousterhout describes in his book "A Philosophy of Software Design" the difference between strategic coding and tactical coding. Where tactical coding relies on hacks to get the job done and strategic coding has a more long term view on the code base. He vouches that software should be done strategically but I believe we need to pick our battles.
There are some entities and functions that are core to engineering companies, but they become core at different times and we should make an attempt to recognise when this change occurs.
It is nice to pretend that your code is an integral part to the business's existence. The truth is a project is always initially an experiment and over time it is rewritten to more accurate specification as we learn and the business learns. Importance is discovered. Your first attempt will be done when you know the least about the topic.
Eventually it might become a core service to many teams within the company and at that point it's worth getting serious about engineering practices. Front loading your engineering standards is making your experimentation more expensive.
If you're treating every project like a personal flower garden you'll struggle to recognise when code is dead weight. Thank it for its cycles, praise it for the outage it caused, the war story and what you have learnt. Then delete it.
Software is about discovery. Code generation enables us to prototype and discover how things work. Prototype for your own education not just for the customer. The pace of learning has increased and we can discover and experiment quicker than ever.
-
Like I did in "Software is planning" ↩
Mon 02 March 2026
Get more out of 1:1s
There isn't a course or career training when it comes to the one to ones with a line-manager. Every manager conducts them in their own way, and what happens in them can sometimes feel secretive. So here's a look under the curtain on how to get more from these meetings.
When you realise that your manager is human these meetings become easier. Once you start giving more accurate descriptions on where you'd like to grow, what you want to work on and share what you've achieved the meeting becomes more productive.
Unfortunately these meetings rely on how well you know yourself. Since managers don't possess an innate ability to know what makes you happy or what your goals and aspirations are. Opening up can feel like a sign of weakness or make you feel more vulnerable, but the more you share the easier you make it for them to help you achieve your goals.
So what does a good one to one look like?1
You set the agenda
We should approach setting the agenda pragmatically, if you are short on time come up with a few question that will lead to a productive outcome that you can ask in every catchup. The one question I typically ask is: How does the rest of the team feel?
This allows me to determine how confident other might be about a project. It helps me become aware of colleagues that are feeling stretched and could use my help or perhaps they wish me to help out in different ways. I've found this to be an effective way to find the gaps that need filling.
The areas you should be creating questions around should be on your general feeling about work, work-life balance, your growth, your interactions with others on your team and company and the progress you've made on tasks and goals. You don't need to fit this all into one catch up but theming your next catch up on one of these topics can help you prepare and get you started.
Achievements should be brought up. This is emphasised in a remote company, where your achievements can be missed if you're telling no one about them. A good manager will pass your achievements around the office, because typically their performance is tied to your own and you're trying to achieve as a team.
Sharing achievements builds trust, once those big ticket items land on the team's plate and they need someone to lead it. The rapport you've built up with your manager might just land you these challenges.
Coach your manager
If you layout sensitive scenarios for your manager this can help you broach a topic or help you handle those situations if you are ever in that position. It can also establish expectations and intent. As an example you could ask how they would let you know if you were underperforming or what an early sign might be.
Your manager is a sounding board. If you have someone in your team that isn't meeting expectations asking them for help is ok, but be sure to prepare how you'll handle the situation so it doesn't look like you're trying to avoid responsibility.
Align yourself with your manager's priorities. This can help you be more aligned with the business objectives and become more valuable to your manager. If you help them they'll probably want you to stick around and if their scope increases so might yours.
Give your managers work to do. If you find yourself working on something that is not challenging or can be tedious try giving it to your manager. If you pitch them something more important that you could be delivering they'll take the tedious blockers away from you.2
Sharpen your ideas
Only call the vote once you know you're going to win
There's an overhead to collaboration, and if you had to listen to every idea in the business you wouldn't have time to do any work. Use your manager to sharpen your ideas, convince them before convincing anyone else. If you slowly get people onto your side through catch ups when you present the idea to the business you'll already know how to answer the probing questions.
Use the time to develop your relationship and your ideas. Your manager has insight into who is working on what and they can direct you to the people that are excited to talk to you about your ideas and these people might know the challenges you are heading into.
Know Yourself
The classic "where do you want to be in 5 years" question isn't asked to determine if you will have a future at the company. The question is used to determine how well you know yourself. If you are managing someone that is unsure about what they enjoy or what makes them happy how are you going to put them on the work they are most passionate about.
It takes work to understand yourself so mentioning this to your manager can allow them to throw all sorts of tasks at you to see what you're best at. They can help you identify your weaknesses and strength. However they'll not do this unless you're comfortable with the challenge and the way to signal that you're comfortable is to ask.
Your manager will also provide you with work you enjoy and having a history of catch ups in which you've said "I really enjoyed working on x" can increase the chance you will work on those things.
Use your manager to polish your strengths and strengthen your weaknesses. Much of this comes with knowing yourself.
Discuss your weaknesses
The best way to deal with a weakness is by opening up to your manager. Then you can work to find situations that will allow you to improve and make mistakes. Don't expect them to provide you a magical solution to your weakness, some weaknesses take time to develop into a strength and skills take practice.
They can provide you some structure and ideas for actions but they can also provide you with work that allows you to stretch yourself. The best way to learn is to make note of mistakes so getting the opportunity to make more of them is worth while.
Not just your manager
When you're in a company you have access to people with all sorts of skills. People that know stuff that you don't, and you can utilise this for your own growth and understanding.
Try catch up with someone in sales and ask questions like, "What about sales would you like more software engineers to know?"
Or set one up with someone in product and ask how they ensure we are working on the best thing?
Finally
To summarise a lot of these meetings depend on what you want to get out of them and this article touches on some of the topics you might wish to dive into during these catch ups. Although they might be biased towards what I try to get out of them.
Perhaps managers could ask you to set up questions for the next meeting but they probably wish to avoid putting any unnecessary pressure on you as there can be personal reasons why someone doesn't wish to gun for more responsibility at this time.
The key is having more empathy with your manager, they probably have their own goals so use them as an example.
Mon 23 February 2026
Review: A Philosophy of Software Design
Last year I read the book 'A Philosophy of Software Design' by Ousterhout. After finishing the book I wrote a review and reading it back I thought I should follow up with a reflection, the reflection is not yet written this is just the review.
The Review
The book offers an alternative perspective on existing approaches to software design in ways that are more pragmatic, with a strong focus on the core principle in software engineering; to tackle complexity.
Ousterhout clearly understands the power of abstraction as a tool for managing complexity and he applies this himself by creating his own metaphor for how software should be designed. He describes systems as having interfaces that should be shallow and implementations that should be deep. A simpler interface will provide less cognitive load than one with a larger surface area and if the implementation is deep it will provide significant power to the user.
Interestingly he also attempts to avoid jargon while describing things such as preferring to say "avoid duplication" instead of spelling out DRY. I believe he does this to allow the reader to grapple about how they are designing software instead of falling back on a adages like DRY as a core tenet without applying thought into what one is actually aiming to achieve. In other words I believe he wants the reader to make intentional choices when it comes to designing software.
Some of the example he uses are obvious to those that have experience reading code, most notably his sections on code comments, however I think his assertion that people don't write documentation upfront because they view is as "drudge work" is true.
He only starts mentioning cognitive leverage in concluding chapters of the book, I think it should be quite a core principle but perhaps a foundation is needed first to then understand how one can use leverage.
A lot of the book I agree with but I'd perhaps want to provide an alternative view on some of the areas he mentions.
The book makes a strong argument that we need to think more about software design than we are doing, I think there's also a balance to be made here. A good developer should be able to figure out if software should be done tactically or if it should be done strategically. Tactical coding does lead us to worst code in the long term and I've seen tactical more often than not, however I don't believe we should constantly be under strategic programming but as an industry and for early career developers we need more of it. Some software doesn't need design but that's an edge case and shouldn't ever be used as an excuse to avoid thinking through a problem.
In my view where developers lack the most thought and tend to create shallowest interfaces are when creating modules.
Shallow modules are a plague in the industry, I've seen very few examples where a developer has realised that modules are themselves an abstraction and a module's design should be considered. Too often we look at the method signature or class methods and believe this is where we create interfaces and abstraction. We must not forget the file or package is itself an abstraction and we should be thinking intentionally about its interface.
I enjoyed his emphasis on thinking "about different pieces of knowledge that are needed to carry out the tasks of your application". Finding the correct balance is a developer skill that can only improve when you consider and make intentional decisions about how you lay out your code, often avoided (and now offloaded) by engineers as it requires us to put in more effort with thinking.
One of the most important elements of software design is determining who needs to know what, and when
Page 43.
When solving a problem, it is far easier to rationalise and decide on your best course of action when that problem can fit in a paragraph. Isolating the relevant information in one space helps, having to go back and forth between pages or modules is a clear indication that the design has a flaw and my impression is that Ousterhout would agree with this. Further to this point, I believe an IDE can make finding all relevant parts of the code easier but doesn't address the core issue, and over reliance on the IDE will suppress issues and complexity creep.
TDD
I don't agree with his take on test-driven-development, in some cases it forces the developer to start thinking about the interface as a user. Writing the test gets you thinking about the module as a user of the module, since a simple interface would hopefully lead to simpler tests. Some of the key design flaws in an original design or even after the design has been done comes from actually using the module.
Leaving comments
I have come to realise that we have access to many tools that can be used in software design and focusing on using them to address complexity should be their main concern. Many of the examples with comments are in their usage, because we have access to comments doesn't mean we should use them for everything and we should be open to discovering more creative ways to leverage comments to tackle complexity.
Reading the code of others helps us discover interesting ways these tools may be utilised and helps us improve in these areas of software engineering.
One of the key points he makes is about determining the obviousness of the software which connects with how I've approached writing code and how I believe the design of software should be faced.
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
Antoine de Saint-Exupéry
Mon 19 January 2026
Chess Programming - Moving
The "Chess Programming" series:
-
2: (here) Chess Programming - Moving
Attempts to create an automatic chess calculator date back to 1913. Chess engine software has had many optimisations and many minds involved in its evolution.
Last year I wrote on how chess programs track the position of every piece. This article is a slice of the chess engine research I compiled when constructing my own chess AI and covers how a chess engine resolves where a piece can move on the board.
You canplay against that chess engine here.
Bit Operations
So far we've covered how to use a 64 bit integer to track the positions of each piece and we've covered how to use bit operations to determine their type and colour.
Now the goal is to compute a 64 bit int that represents the potential positions that a piece can either move-to or attack. This number is called the "moveboard".
If we have a moveboard for a particular piece we can & the
positions of the opposing pieces in order to get attacking
positions and we can subtract the positions of all pieces in
order to avoid moving a piece to an occupied position.
If we simplify this to a single position on the board.
Given that we are able to move to this position, i.e. the
moveboard is 1, and if we consider it occupied by the
opposition, i.e. having a variable called "theirs"
being 1.
Computing the attacks is determined by doing the following:
>>> moveboard & theirs
1
This tells us the position is a valid attack. If the
opposition does not occupy that position (theirs == 0).
Computing for attacks would result in 0. There's nothing
to attack.
To compute valid moves, if theirs is 0 and ours is 1, we
do the following:
>>> moveboard & ~(theirs | ours)
0
This indicates that we can't move to this position. If the result was 1 then it would be empty and it would be a valid position for our piece to move. If we use 64 bit integer instead of a single bit, as shown in the example, the processor can determine the valid moves and attacks across the entire board in a single operation.1
Pawns
There's an edge case with pawns however, they do not use a
moveboard to determine the valid moves and attacks as
the positions a pawn may attack are different to where it
may move. Despite this they are still relatively simple.
Given a pawn in the middle of the board we only need to shift it up to determine its set of moves and after subtracting occupied positions we can determine if the pawn can move ahead or not.
# Shifting up
pawn_position >> 8

To determine if that pawn can attack we shift the pawn twice, "up and left" as well as "up and right". We then check to see if the opposition occupies these positions, this will determine valid attacks for the pawn.
# Shifting "up & left" as well as "up & right"
pawn_position >> 7 && pawn_position >> 9

Knights
Things get more interesting when we compute valid moves and attacks for a knight, but how does a knight move?
The knight is interesting because it's the only piece that is unrestricted by the position of other pieces. I.e. it can jump over pieces to get to where it wants to go. A knight in the centre of the board can move to the following locations:

If we had a
knight sitting on the ith index represented by the "bitboard"
variable we can compute the moveboard for the knight by doing the
following shifts:
(
((bitboard & NOT_H_FILE) >> 15)
| ((bitboard & NOT_A_FILE) >> 17)
| ((bitboard & NOT_GH_FILE) >> 6)
| ((bitboard & NOT_AB_FILE) >> 10)
| ((bitboard & NOT_H_FILE) << 17)
| ((bitboard & NOT_A_FILE) << 15)
| ((bitboard & NOT_GH_FILE) << 10)
| ((bitboard & NOT_AB_FILE) << 6)
)
There are four knights on a chess board, so this calculation would need to be done 4 times for every new board state. Since the performance of a chess engine is largely determined by how far into the future it can look, reducing the number of calculations on each board state can enable the engine to perform a deeper search.
To avoid these computations, we can simply pre-compute the moveboard for every square on the chess board before the game begins. We can then provide the engine with a lookup table, mapping all positions to their moveboard. Avoiding the overhead of computing them during the game.
Sliding
We also do this for the sliding pieces on a chess board. The rooks, bishops and queens move in a way that are considered as sliding.
Again we pre-compute all these positions and store the result in a map, however this isn't the final moveboard for sliding pieces.

Pieces closer to a sliding piece block its path and unlike the knight sliding pieces can't jump over these obstructions. So the resulting bitmask can't be used to determine the valid moves and attacks on its own.
The bitmask is used to identify the potential blockers along a sliding piece's direction of movement. Indicated in orange below:

Once we have identified the potential blockers we use this value to map to the final moveboard. This allows us to look up any moveboard using all combinations of potential blockers during the game and avoids having to shift the piece's position along its sliding axis for every new board state.

As explained we can use a moveboard to compute the
attacking positions (moveboard & theirs). As well as the
valid positions for moving (moveboard & ~(theirs|ours)).
In summary the following steps are needed to find the attacking positions and moving positions for a rook:
- Given the rook's position lookup the bitmask of an
unobstructed rook.
(index -> bitmask) - Use the unobstructed bitmask to find potential
blockers given all the pieces on the board.
(bitmask & (theirs|ours)) - Using the locations of all the blockers lookup the final
moveboard for the rook.
(blockers -> moveboard).
Computing potential blockers can be simplified as there are positions on the board that would never block a sliding piece. Such as positions on the edge of the board.
Given a rook in any of the positions indicated in red we can ignore positions in black as having "potential blockers".

This reduces the overall number of potential blocker combinations we need to consider, improving the engine's start-up time and memory footprint.
We do the same for bishops, except these potential blockers are computed diagonally and finally we compute the moveboard for the queens by combining the outcomes of the rook and the bishop.
Playing Chess
Now that we are able to compute the set of possible moves and attacks for each piece on the chessboard. Picking a piece at random and picking one of the positions from it's set of move and set of attacks will allow a computer to play against us.
This chess engine would still make illegal moves. To play a legal game of chess we would need to consider if the king is in check or if moving a piece might result in your own king being in check.
Finally; in-order-to implement all legal chess moves we would need to extend our move sets to include pushing the pawn twice when it is on its starting position, en passant and the ability to castle your king.
It might be more interesting, however, to dive into how a chess engine evaluates the board state so that it can make an informed decision when it moves a chess piece.
-
Assuming you have a 64 bit processor. ↩