Semantic indenting can greatly increase readability and maintainability of code. It can even increase the quality of code! (eg by making excessive dependencies more obvious etc)
How many times have you come across code indented like this (ignore the code, it's just gibberish, focus on the indenting itself)
@Autowired
public FooService(FirstDependency first, SecondDependency second, ThirdDependency third,
FourthDependency fourth, FifthDependency fifth, SixthDependency sixth,
SeventhDependency seventh) {
//...
}
@Override
public List<Thingos> getThingos(int number, Context context,
String anotherArgument) {
if (theSky.getColor().equals("blue") ||
(theSky.getColor().equals("red")
&& featureSwitch.isEnabled(AppFeature.OUR_FEATURE))) {
doSomething(number, context, anotherArgument, "some other argument",
number * 2);
} else {
//...
}
}
}
Even in this short example, it takes a lot of effort just to parse what code is at what semantic level. Code indented like this is needlessly confusing, and the more code, and the more nested the code, the bigger the problem.
Consider instead if semantic indenting was used
@Autowired
public FooService(
FirstDependency first,
SecondDependency second,
ThirdDependency third,
FourthDependency fourth,
FifthDependency fifth,
SixthDependency sixth,
SeventhDependency seventh
) {
//...
}
@Override
public List<Thingos> getThingos(
int number,
Context context,
String anotherArgument
) {
if (
theSky
.getColor()
.equals("blue")
|| (
theSky
.getColor()
.equals("red")
&& featureSwitch
.isEnabled(AppFeature.OUR_FEATURE)
)
) {
doSomething(
number,
context,
anotherArgument,
"some other argument",
number * 2
);
} else {
//...
}
}
}
Now, it's immediately obvious what code is at what semantic level. Especially, the risk of confusing what level the ||
and &&
is at in the if statement is basically eliminated.
Yes, there probably are. That's a smell that the class and its constructor and methods probably have too many dependencies and arguments. The fact that we can easily see that now is a benefit of semantic indenting, not a problem - without semantic indenting, the same problem is there, it's just obfuscated.
To be clear, even in code that doesn't have too many dependencies and arguments, semantically indented code definitely does result in more lines - the code starts to look more like SQL than imperative instructions. I believe this can subtly encourage a more declarative, functional coding style, which I consider a benefit. (others may disagree)
No, it doesn't. Java's standard indenting looks like this
public List<Thingos> getThingos(int number,
Context context,
String anotherArgument) {
//...
}
//things that are really at the same semantic level ending up in different places
public List<Thingos> anotherMethodWithAMuchLongerName(int number,
Context context,
String anotherArgument) {
//...
}
List<Integer> myListOfNumbers = Arrays.asList(1, 2, 3);
myListOfNumbers.stream()
.filter(number -> number > 0) //indent second call
.map(String::valueOf)
.collect(Collectors.toList());
with semantic indenting, that would look like this
public List<Thingos> getThingos(
int number,
Context context,
String anotherArgument
) {
//...
}
public List<Thingos> anotherMethodWithAMuchLongerName(
int number,
Context context,
String anotherArgument
) {
//...
}
List<Integer> myListOfNumbers = Arrays.asList(1, 2, 3);
myListOfNumbers
.stream() //indent first call
.filter(number -> number > 0)
.map(String::valueOf)
.collect(Collectors.toList());
which, if you think about it, really makes more sense from an indenting point of view.
And... there's really nothing wrong with deviating from a standard if you have good reason to. There are countless examples of things that used to be standard in Java (and other languages) that people realized weren't the best way to do things, and over time, the different way of doing them became the new standard.
Fair point, but long lines using the full width of the screen are still best used only when the statement a) fits on a single line and b) doesn't have too many semantic levels. (even statements that fit on a single line can sometimes have too many semantic levels)
eg, this is fine!
every { user.permissions } returns setOf(Permission.USER_SEARCH_BY_USER_ID, Permission.USER_SEARCH_BY_USER_IDS)
whereas this is already less fine (to be clear, it's still fairly easy to read, but when there's lots of lines like that, it quickly becomes increasingly annoying to read the code)
every { user.permissions } returns setOf(Permission.USER_SEARCH_BY_USER_ID, Permission.USER_SEARCH_BY_USER_IDS,
Permission.USER_SEARCH_BY_EMAIL, Permission.USER_SEARCH_BY_PROPERTY)
and could arguably use semantic indenting like this
every {
user.permissions
} returns
setOf(
Permission.USER_SEARCH_BY_USER_ID,
Permission.USER_SEARCH_BY_USER_IDS,
Permission.USER_SEARCH_BY_EMAIL,
Permission.USER_SEARCH_BY_PROPERTY
)
Worth mentioning also is that, while we have lots of screen real estate in our IDEs, and with our external monitors in the office, when working with just a laptop screen, or especially in the GitHub split window pull request review GUI, that's not always the case. (GitHub will actually make poorly indented code even worse by inserting soft linebreaks)
Granted, indenting really is literally just cosmetics, and I do think it's pointless to be fundamentalist about it and fight about it.
But I think most people would agree that not giving any thought to indenting is objectively bad, irrespective of which style or standard you prefer. And, as I hope I've shown, indenting can actually reveal underlying real problems with the code.
Also, note that I'm not making some fundamentalist argument that absolutely everything must be semantically indented! Semantically indenting absolutely everything, like this
List<Integer> myListOfNumbers = Arrays
.asList(
1,
2,
3
);
would definitely be excessive and make the code harder to read, not easier. Use your judgement!
Honestly, I believe your first snippet to be much more readable than the second. Humans are good at understanding patterns. Those arguments may be on the same line because they are "together" conceptually (e.g. line_width, line_length for the first line aggregating geometric parameters, fill_color, line_color for the second to aggregate color parameters etc)
There has been a trend, with automated formatters such as gofmt and black, to destroy compact, perfectly readable code to favour machine and diff-oriented formatting, with the idea that "you must have the shortest diff possible". But you read code all the time, you read diffs only once, at review.
This kind of formatting literally makes my eyes bleed. It destroys important hints, it lengthens the code, and forces you to read code like you would read
a
sentence
written
almost
line
by line
like
this