My Personal Experience with Reading Challenges
Code readability is subjective—some developers love compact one-liners, while others prefer structured formatting. But what if the way we format code is influenced by how our brains process language? For me, dyslexia has shaped my approach to reading, and by extension, my coding style.
I don’t have an official dyslexia diagnosis, but I’m quite certain I had it as a child and still experience some effects today, though they are less noticeable. Reading and writing were significant challenges for me growing up. My reading speed was extremely slow—even in my later school years, I could only manage about ten pages per hour, and afterward, I felt exhausted. Short texts, like mathematical problem statements, were easier to handle because I could reread them multiple times.
My struggles were especially noticeable when reading aloud—I would mix up letters, mispronounce words, and often stutter. I remember my father once telling me, even in my later school years, that I still read the same way I did when I first learned in preschool. Writing was equally difficult—I frequently skipped letters and confused similar-looking ones, like k and l or g and d.
Now, at 42 years old, I still read at a relatively slow pace. I took a couple of speed-reading lessons and learned techniques to read faster, but I’m still much slower than the average reader. Before that, I couldn’t keep up with movie subtitles and had to pause frequently when watching foreign-language films. Thankfully, I’ve improved over time, and now I can follow subtitles without struggling.
A few years ago, I joined a book club where we selected a book to read each month and then discussed it. It was a great experience—I read books I wouldn’t have chosen otherwise. However, I eventually left because keeping up with a book per month was too demanding. It took a significant amount of my free time just to catch up.
The Game-Changer: E-Book Readers
One of my biggest discoveries was using an e-book reader, particularly for EPUB files. I had owned an e-reader for a while but mostly used it to read technical PDFs. However, switching to EPUB was a game-changer—I immediately noticed how much easier it was to read when lines were shorter and the font was larger.
Here’s a screenshot of a page from Wool by Hugh Howey in my native Lithuanian language:
In contrast, here’s an example of a book that was much harder for me to read:
These images don’t show the exact scale, but I hope you get the point. For me, the most important factors are line length, line spacing, and text contrast.
How It Feels
When I struggle with dense text, it feels like standing at the edge of a great height—overwhelming and disorienting.
These reading difficulties aren’t just about books—they extend into how I read and write code. I’ve formatted my code this way for a long time, but only recently did I start to wonder if my dyslexia is the reason behind it. Here’s why I structure my code the way I do.
Code Formatting: Why I Hate One-Liners
Many years ago, I had a discussion with a former coworker about code line length. He had a large 23–24-inch screen with his code editor taking up the entire display. His panels (errors, solution explorer, etc.) were on a second monitor. He wrote extremely long lines of code—some stretching across the full width of the screen.
I remember asking him why he did that, and he confidently replied, "I want to use the entire screen efficiently!" 😐
Unfortunately, I have trouble reading long lines of code—especially when I have to scroll horizontally to see everything.
Example of Hard-to-Read One-Liner
Here’s a typical example from CodeWars:
public static int[] MoveZeroes(int[] arr)
{
return Enumerable.Concat(arr.Select(n => n).Where(n => n > 0), arr.Select(n => n).Where(n => n == 0)).ToArray();
}
This single line technically works, and some might even be proud of it. But for me, it’s difficult to read. I immediately reformat it like this:
public static int[] MoveZeroes(int[] arr)
{
return Enumerable
.Concat(
arr
.Select(n => n)
.Where(n => n > 0),
arr
.Select(n => n)
.Where(n => n == 0)
)
.ToArray();
}
Yes, this version takes more lines. But I can now immediately see the logic.
And if we were still paid by lines of code, this would be even more profitable. 😉 (Although I doubt anyone is paid that way these days!)
Guidance: My Approach to Readable Code
In this section, I’ll outline the formatting principles I follow to make code more readable for myself. I'll provide examples of real production code—either as I originally encountered it or with minor adjustments to illustrate the case more clearly.
The focus here is on code formatting, not logic, design patterns, or other best practices.
General Rule: Keep It Visually Manageable
Through experience, I’ve found that code is easiest to read and understand when:
I don’t have to move my eyes left and right excessively. Instead, I prefer to read code in a vertical flow—moving only up and down.
Lines are kept within a comfortable visual span. Long horizontal lines force unnecessary scrolling and make it harder to track code structure.
Important information appears at the beginning of each line. This ensures that method names, key expressions, and logical operators are immediately visible, reducing the mental effort required to parse the code.
By following these principles, I make sure that my code remains structured, easy to scan, and effortless to navigate.
Method calls
Method calls are often written as one-liners, but when they have multiple parameters, they can become difficult to read. Sometimes, auto-formatting turns them into multi-liners, but in a way that breaks lines randomly, making the structure unclear.
Take this example:
Object o = assem.CreateInstance("Example", false,
BindingFlags.ExactBinding,
null, new Object[] { 2 }, null, null);
At a glance, it's hard to see how many parameters there are or distinguish their purpose.
To improve readability, I reformat it like this:
Object o = assem.CreateInstance(
"Example",
false,
BindingFlags.ExactBinding,
null,
new Object[] { 2 },
null,
null
);
Now, each parameter appears on its own line, making it much easier to:
Identify all arguments without scanning horizontally.
Quickly see which values correspond to which parameters.
Spot missing or incorrect arguments more easily.
For me, keeping parameter lists structured as a vertical list greatly improves readability and maintainability.
Method signature
Method signatures can get long, especially when they have multiple parameters with complex types. Keeping everything on a single line or chopped at random places might seem compact, but it makes scanning difficult, especially when method names vary in length or when additional parameters are added later.
For example:
protected override LanguageDataCollection CreateResult(IKey key, Expiry expiry, Source source, LocationEventKey locationEventKey, IDictionary<LanguageKey, LanguageData> dictionary)
{
...
}
Or, when auto-formatting splits parameters at random places, it can look like this:
protected override LanguageDataCollection CreateResult(IKey key, Expiry expiry, Source source,
LocationEventKey locationEventKey, IDictionary<LanguageKey, LanguageData> dictionary)
{
...
}
At first glance, it’s hard to quickly count parameters or see what each parameter represents.
Instead, I prefer to split parameters into separate lines, aligning them one per line:
protected override LanguageDataCollection CreateResult(
IKey key,
Expiry expiry,
Source source,
LocationEventKey locationEventKey,
IDictionary<LanguageKey, LanguageData> dictionary
)
{
...
}
Following this approach makes method signatures structured and maintainable, ensuring they remain easy to work with even as they evolve.
Class declaration
Class declarations are usually short and straightforward, but when they include long inheritance chains or multiple generic parameters, they can quickly become difficult to read.
To maintain readability, I follow the same principles as in method calls:
Keep all significant elements aligned to the left.
Break long lines into shorter segments that fit within a comfortable visual span.
Example: Hard-to-Read Class Declaration
When written in a single line, a class declaration with multiple generic parameters and constraints quickly becomes cluttered:
public abstract class Popularities<TGeoPopularity, TGeoCode, TGeoEntity, TGeoName, TDetailsControl> : MatrixEntityProfileListBase<uint, TGeoPopularity, TDetailsControl>, ISomeInterface
where TGeoPopularity : class, IGeoPopularity
where TGeoCode : IGeoCode
where TGeoEntity : class, IGeoEntity
where TGeoName : class, IGeoName<TGeoCode>
where TDetailsControl : DetailsBase<uint, TGeoPopularity>
{
...
}
By breaking it into multiple lines and aligning elements logically, the class definition becomes much easier to scan and understand:
public abstract class Popularities<
TGeoPopularity,
TGeoCode,
TGeoEntity,
TGeoName,
TDetailsControl>
: MatrixEntityProfileListBase<
uint,
TGeoPopularity,
TDetailsControl>,
ISomeInterface
where TGeoPopularity : class, IGeoPopularity
where TGeoCode : IGeoCode
where TGeoEntity : class, IGeoEntity
where TGeoName : class, IGeoName<TGeoCode>
where TDetailsControl : DetailsBase<uint, TGeoPopularity>
{
...
}
Chained Method and Property Calls
Chained calls, especially in LINQ, can quickly become hard to read when squeezed into one long line:
var result = roomRateDetails.RateInfos.RateInfo.Where(c => SourceConfiguration.IncludeNonRefundables || (!c.nonRefundable & !SourceConfiguration.IncludeNonRefundables))
.Select(rateInfo => ResultFactoryBundle.OfferFactory.CreateInstance(locationEventKey, rateInfo));
Becomes:
var result = roomRateDetails
.RateInfos
.RateInfo
.Where(c =>
SourceConfiguration.IncludeNonRefundables
|| (!c.nonRefundable & !SourceConfiguration.IncludeNonRefundables)
)
.Select(rateInfo =>
ResultFactoryBundle
.OfferFactory
.CreateInstance(
locationEventKey,
rateInfo
)
);
As mentioned in the general rule—all significant information goes to the beginning of the line: method names, property names, parameters, logical operations.
Constructor with a Base Constructor Call
Constructor declarations follow the same formatting principles as methods, but constructor chaining can make them harder to read. When the base constructor call is kept in a single line, it blends with the parameter list, making it less clear.
Instead of this:
protected CommonToolsUtilityBase(DatabaseCredentialRegistry databaseCredentialRegistry, IDbServiceFactory serviceFactory,
DatabaseIdentifier databaseIdentifier, HashUsage hashUsage)
: base(databaseCredentialRegistry, serviceFactory, databaseIdentifier, hashUsage)
{
}
I prefer this:
protected CommonToolsUtilityBase(
DatabaseCredentialRegistry databaseCredentialRegistry,
IDbServiceFactory serviceFactory,
DatabaseIdentifier databaseIdentifier,
HashUsage hashUsage)
: base(
databaseCredentialRegistry,
serviceFactory,
databaseIdentifier,
hashUsage
)
{
}
The constructor parameters are now clearly listed, and the base call is visually distinct, making it easier to see what is being passed where. This also helps when parameters get longer—each part remains readable without needing to scroll horizontally.
Bracket alignment
Sometimes, it’s hard to quickly see where a parameter list ends and where a new statement begins. It gets even trickier when passing method calls as parameters—figuring out whether you're dealing with a parameter of the main method, or a nested method call can take extra effort.
Example:
var enumerable = DataHandler.Instance.Query<Models.AccommodationYouTravelSourceData>(
QueryBuilder.GetQuery(
GetSelectQuery(),
selector,
this),
selector.GetParameters().ToObject(),
DatabaseCredential);
I prefer to align the closing bracket with the method call it belongs to, keeping indentation consistent:
var enumerable = DataHandler
.Instance
.Query<Models.AccommodationYouTravelSourceData>(
QueryBuilder.GetQuery(
GetSelectQuery(),
selector,
this
),
selector.GetParameters().ToObject(),
DatabaseCredential
);
Now, each method call and its parameters are clearly structured, making it easier to follow the hierarchy without second-guessing what belongs where. It also keeps deep nesting visually manageable, avoiding the need to constantly jump between opening and closing brackets.
Line spacing
In general, I find it useful to separate statements with an empty line to improve readability. The only exceptions are simple declarations and assignments, which I usually keep together.
Take this example, where everything is packed closely together:
using (var client = new RestClient(url))
{
client.Authenticator = new HttpBasicAuthenticator(SourceCredential.UserName, SourceCredential.Password);
var request = new RestRequest("ordermanagement/v1/orders/" + cancelParameter.ReferenceTransactionId + "/cancel", Method.Post);
requestResponsePair = new RequestResponsePair(DateTime.Now, request.ToString());
var response = client.Execute(request);
requestResponsePair.AddResponse(response.Content);
}
I prefer to add spacing between logically separate steps, making it easier to follow the sequence of operations:
using (var client = new RestClient(url))
{
client.Authenticator = new HttpBasicAuthenticator(
SourceCredential.UserName,
SourceCredential.Password
);
var request = new RestRequest(
"ordermanagement/v1/orders/"
+ cancelParameter.ReferenceTransactionId
+ "/cancel",
Method.Post
);
requestResponsePair = new RequestResponsePair(DateTime.Now, request.ToString());
var response = client.Execute(request);
requestResponsePair.AddResponse(response.Content);
}
The same applies to conditionals—instead of keeping everything tight, I add spacing to visually separate decisions:
float fKilometers;
float fMiles;
bool kilometersIsdefined = float.TryParse(valueKm, out fKilometers);
bool milesIsDefined = float.TryParse(valueMi, out fMiles);
if (kilometersIsdefined)
{
return CreateDistance(SearchRadiusUnitType.KM, fKilometers, lngCode, text);
}
if (milesIsDefined)
{
return CreateDistance(SearchRadiusUnitType.MI, fMiles, lngCode, text);
}
return null;
Becomes:
float kilometers;
float miles;
bool kilometersIsDefined = float.TryParse(valueKm, out kilometers);
bool milesIsDefined = float.TryParse(valueMi, out miles);
if (kilometersIsdefined)
{
return CreateDistance(
SearchRadiusUnitType.KM,
kilometers,
languageCode,
text
);
}
if (milesIsDefined)
{
return CreateDistance(
SearchRadiusUnitType.MI,
miles,
languageCode,
text
);
}
return null;
Spacing isn’t just about aesthetics—it helps me process the code faster.
Mind indentation
I’ve worked a bit with Python, and one thing I really liked is how indentation is enforced by the language itself. It naturally leads to more readable code by clearly distinguishing higher-level statements from lower-level ones.
In languages where indentation isn't enforced, improper formatting can make code harder to follow:
var petraBooking = await SearchAndBookItinerary(DistributionOption.Source,
DistributionMethod.Email,
PetraSourceTestData.BillingContact,
PetraSourceTestData.Passengers,
new TrainTravelMethodOptions(false, false, false, false));
Instead, I prefer to structure parameters clearly, keeping indentation consistent and logical:
var petraBooking = await SearchAndBookItinerary(
DistributionOption.Source,
DistributionMethod.Email,
PetraSourceTestData.BillingContact,
PetraSourceTestData.Passengers,
new TrainTravelMethodOptions(
false,
false,
false,
false
)
);
Again, all significant information is aligned to the left. No need to move my eyes side to side and then up and down—everything is visible at a glance, top to bottom.
Sometimes, the IDE automatically aligns parameters based on the first parameter’s position, leading to a stepped or cluttered look, especially when method names vary in length. This inconsistency makes scanning difficult and adds unnecessary indentation levels.
For example:
info.AddRange(roomResponse
.Elements("ValueAdds")
.EmptyIfNull()
.Select(x =>
new EssentialInformation(Key.Empty, Expiry.Empty, CommonDataHolders.Source.EanHotels,
localeCode, x.StringValue("ValueAdd"), null, EssentialInfoType.Vendor)));
info.AddRangeLongerMethodName(roomResponse
.Elements("ValueAdds")
.EmptyIfNull()
.Select(x =>
new EssentialInformation(Key.Empty, Expiry.Empty, CommonDataHolders.Source.EanHotels,
localeCode, x.StringValue("ValueAdd"), null, EssentialInfoType.Vendor)));
Instead, I keep all method calls aligned to the left and indent parameters only one level for clarity:
info.AddRange(
roomResponse
.Elements("ValueAdds")
.EmptyIfNull()
.Select(x =>
new EssentialInformation(
Key.Empty,
Expiry.Empty,
CommonDataHolders.Source.EanHotels,
localeCode,
x.StringValue("ValueAdd"),
null,
EssentialInfoType.Vendor
)
)
);
info.AddRangeLongerMethodName(
roomResponse
.Elements("ValueAdds")
.EmptyIfNull()
.Select(x =>
new EssentialInformation(
Key.Empty,
Expiry.Empty,
CommonDataHolders.Source.EanHotels,
localeCode,
x.StringValue("ValueAdd"),
null,
EssentialInfoType.Vendor
)
)
);
The same applies in the following example:
dictionary.Add(new CurrencyDataKey(currencyCode),
new CurrencyData(Key.Empty,
Expiry.Empty,
CommonDataHolders.Source.EanHotels,
currencyCode,
price,
null,
DateTime.Now));
return new CurrencyDataCollection(Key.Empty,
Expiry.Empty,
CommonDataHolders.Source.EanHotels,
key,
dictionary);
I reformat it to keep everything aligned logically:
dictionary.Add(
new CurrencyDataKey(currencyCode),
new CurrencyData(
Key.Empty,
Expiry.Empty,
CommonDataHolders.Source.EanHotels,
currencyCode,
price,
null,
DateTime.Now
)
);
return new CurrencyDataCollection(
Key.Empty,
Expiry.Empty,
CommonDataHolders.Source.EanHotels,
key,
dictionary
);
Conditional statements
Following my general rule of keeping significant information at the beginning of the line, I prefer to place logical operators at the start of each line, rather than at the end. This makes conditions easier to track, as all operators are aligned in one column instead of being scattered at the end of lines with different lengths.
Instead of this:
if (Math.Abs(nightlyRateTotal) < 0.0001m || chargeableRateInfo.Surcharges == null ||
chargeableRateInfo.Surcharges.Surcharge == null || chargeableRateInfo.Surcharges.Surcharge.Length < 1)
I format it like this:
if (Math.Abs(nightlyRateTotal) < 0.0001m
|| chargeableRateInfo.Surcharges == null
|| chargeableRateInfo.Surcharges.Surcharge == null
|| chargeableRateInfo.Surcharges.Surcharge.Length < 1)
This way, each condition is clearly visible on its own line, and the logical flow is easy to scan without jumping to the end of different-length lines.
The same applies to ternary operations. Instead of this:
string message = (age >= 18 && country == "USA") ? "Eligible to vote in the United States elections." : "Not eligible to vote in the United States elections.";
Or this:
string message = (age >= 18 && country == "USA") ?
"Eligible to vote in the United States elections." :
"Not eligible to vote in the United States elections.";
I prefer:
string message = (age >= 18 && country == "USA")
? "Eligible to vote in the United States elections."
: "Not eligible to vote in the United States elections.";
Here, the ternary operator (?
and :
) appears at the beginning of each line, making it immediately clear that this is a conditional expression.
Arithmetic Operations
Just like with logical conditions, I prefer to keep arithmetic operators aligned at the beginning of each line. This makes complex calculations easier to read, prevents operators from getting lost at the end of long lines, and helps spot errors faster.
Take this example, where operations are packed into a single line:
double finalValue = (baseValue + extraCost) * taxRate - discount / rebateFactor + serviceCharge;
When expressions become longer, it becomes harder to quickly see the order of operations and how different terms relate.
Instead, I format it like this:
double finalValue = (baseValue
+ extraCost)
* taxRate
- discount
/ rebateFactor
+ serviceCharge;
Ways to Save Visual Vertical Space
Keeping lines short improves readability, but it naturally expands the code vertically. If not managed well, this can make it harder to grasp the core algorithm of a method at a glance.
One effective way to control vertical expansion is by splitting code into private methods with meaningful names. This keeps the main logic clean, making it easy to understand the flow at a high level, while detailed operations remain well-structured within smaller methods.
This way, the core algorithm stays clear, and each method remains focused, readable, and well-formatted.
Final Thoughts
Code formatting preferences vary from person to person, and agreeing on a style within a team can be difficult. Some developers love one-liners, while others (like me) find them unreadable. It took me a long time to realize that my preference for structured, short lines isn’t just a matter of style—it might be directly connected to the way I process text due to dyslexia.
But beyond personal preference, readability is about more than just individual comfort—it affects how maintainable, collaborative, and bug-free our code is. A well-structured codebase isn’t just easier for you to read today; it’s easier for your teammates (or even future you) to understand and work with tomorrow. Code that is clear, easy to scan, and logically structured reduces cognitive load, making development faster and debugging less frustrating.
At the end of the day, code isn’t just written for machines—it’s written for people. Whether you have dyslexia or not, finding a formatting style that makes code easier to read is an investment in better development practices. Readable code leads to better collaboration, fewer mistakes, and a smoother workflow.
What about you? Do you have a preferred way of formatting code? Have you noticed patterns in how you structure code that make it easier for you to read? Let’s discuss in the comments!
Top comments (0)