Imperative
The systematic elimination of undefined behavior in your codebase is something that you and every developer—regardless of experience or professional achievement—hold as a guiding imperative. You want consistent results and efficiency with regard to output, and you want it hardened against all foreseeable circumstances and scales. You won’t abide ambiguity and uncertainty in the product that you ship. But do you apply that same standard to your development ethics?
Three Pillars
In Buddhism, the term that is used for “skillful conduct” is sīla, and it is the foundation for living an impactful life. What the engineering world currently has in lieu of sīla are what I am going to call the three pillars of situational conduct. “Best practices” is idealistic advice that is commonly ignored. “Compliance” is externally dictated policy that is constantly subject to change. “Code of conduct” is often performative and pandering to a shifting and hypothetical consumer opinion. Together these three pillars show that software engineering doesn’t really have a vocabulary of its own to conceptualize what we, as human beings, ought and ought not do.
Intentional Development Ethics
As it is important to define behavior in your code whenever possible, so it is with your development ethics. It should be done in advance as part of the planning phase. Can you ethically respond to situations on a “gut level” based on their context? Absolutely, but it would not be intentional or optimal. Instead, it translates to a significantly higher probability that the resulting behavior is not ethical—or at best, situationally ethical. We can all do better.
For my own coding projects, I have created a document outlining my development ethics. They are not suggestions or perceived as goals. They are not aspirational. They are constraints. If I am working with an LLM, I include a markdown document of those constraints for it to check me against. If something I am developing violates those predefined boundaries, I don’t do it. I find a different way to solve the problem or I accept the limitations of an ethical solution. This is not just moral conduct, it’s socially responsible.
The Four Precepts of Development Ethics
Refrain from Violating Privacy
Privacy should always be the default setting. Don’t collect data that you don’t have a specific or immediate use for, don’t store it for any longer than needed for that purpose, and don’t participate in any cross-site tracking or analytics. All behavior tracking stays local, anonymous, and relevant to the application. Don’t objectify and commodify users by selling their data without compensation and consent. Follow EFF’s Do Not Track Policy.
For me, the hardest part of this is data retention. As a dataphile, my own conditioning impels me to keep as much data around for as long as possible. It’s as if the ability to peer into trends from five years ago will help me divine the future. But there is no absolute correlation between the past and the future, and I know this intellectually and experientially. We don’t need to hoard data. We should be able to produce workarounds for training models and generating metrics that can work perfectly well with a rolling 10-day data retention strategy—what solutions haven’t been explored at all because of our Big Data addiction?
Refrain from Dark Patterns
Design for user value over engagement metrics. Notifications, emails, and messaging are for the user’s benefit first, engagement metrics afterwards. Do not use UI in deceptive ways—buttons should do what they say, and features should not be hidden or obscured. Do not funnel or herd user behavior to profitable options or away from unprofitable options with the UI. Do not generate artificial urgency or scarcity, and do not attempt to algorithmically or psychologically pressure user decisions in any way. If added features cost money, that should be made clear. In general, be honest and transparent with users about what your software does.
I desperately want people to engage with the things that I make. I acknowledge that, and I try to be realistic about it. It’s very human to want to be liked and to desire success. I know that it has the potential to drive me towards unwise behavior, and that I’m not always going to be fully aware of it. This is another reason to set development ethics intentionally and ahead of time, because it makes it easier to identify when I might be manipulating the code to manipulate others: I can design algorithms to feed the worst type of human behavior because it hits engagement metrics; I can hide opt-outs or data deletion features when the contrary behavior benefits me. But in the context of sīla, it doesn’t benefit me in the long run. In that scenario, I am perpetuating the standard. I’m promoting behavior to which I myself will also fall victim.
Refrain from Convenience over Security
If people trust you with their data, protecting that data is not optional and is not a tiered feature. Use established, battle-tested libraries, cryptography, authentication, and session management. There is no reason to DIY any of these areas. Do not store user data in plaintext. Sanitize user input. Keep dependencies updated. Assume your system will be breached, and plan to limit vulnerability under those circumstances. Document and address vulnerabilities and breaches in a transparent way.
User management is one of my least favorite things to even think about when developing a web application. There are a lot of moving parts and multiple approaches, but none of them are simple or as out of the box as some lead us to believe. It’s easy for me to want to take shortcuts and leave out some of the complexity—no brute force protection, no sanitization, no email verification, no session management. It’s so much repetition and so much boilerplate. My life (and my users’ lives, probably) would be so much simpler if I just didn’t have to do all of that. But all of that complexity and boilerplate code is essential to protect the user as well as the product. If the access needs to be restricted, then that restriction absolutely needs to be done correctly. It’s not easy, but it’s necessary. I need to make a commitment to it.
Refrain from Environmental Indifference
Favor efficiency over resource-heavy solutions when the outcome is comparable. Don’t run unnecessary services, background processes, or redundant API services. Be mindful of the computational and energy cost of complex LLM and ML workloads. Seek out solutions that reduce your footprint whenever possible, even if those solutions are less profitable.
I am awed on a weekly (if not daily) basis by what LLMs are able to do in this new age, and I am an advocate of the responsible use of AI and its development. But in being responsible, I have to acknowledge that there are very real environmental issues with how LLMs/AI models are implemented as well as how I use them. It’s not sustainable in its current state, and so it means that sometimes I have to pull back and do something myself, even though it will take me 10 minutes and an AI could finish it in 30 seconds. It also means that I have to do what I can to encourage decision makers to make better decisions about where they build data centers and how that affects the environment. The mad rush to cram more GPUs into more spaces to give us larger, faster, better models needs to be tempered by the understanding that we are already experiencing a critical environmental crisis, and the decisions that we make now will affect our lives for decades to come.
ETHICS.MD as a Personal and Living Document
These precepts are basic, and there are (and will be) many more granular details that I list within my own development ethics document. There will absolutely be some that are personal to me and of little relevance to others, and I would expect the same to be true for those who find value in adopting this ETHICS.MD practice. For me, it’s not about others doing exactly as I do. The important take-away is to do it in a manner that resonates as true to you, and that intentionally sets ethical boundaries for all future development projects. It’s this type of “vow” that will systematically dispel undefined behavior in a way that I find deeply important. Key to that understanding is remembering what inevitably happens when we allow undefined behavior to persist in our codebase: it crashes.