pinterest / ktlint

An anti-bikeshedding Kotlin linter with built-in formatter
https://pinterest.github.io/ktlint/
MIT License
6.06k stars 504 forks source link

ktlint has become too opinionated #2703

Closed meyertime closed 1 week ago

meyertime commented 2 weeks ago

The direction ktlint has been taken the past couple years has removed it from the needs of the community and alienated some of its users.

Previously, ktlint aligned with the official style guides. Now, the default code style, ktlint_official, is opinionated and conflicts with and exceeds the official guides. Since the guides no longer serve as an anchor, this default style ends up reflecting the personal opinion of the maintainer. Issues https://github.com/pinterest/ktlint/issues/2138 and https://github.com/pinterest/ktlint/issues/2423 are good examples of this, because the changes proposed have strong community support, and they are aligned with both official style guides, yet they have been refused.

The intellij_idea and android_studio code styles may be used for those who don't agree with the opinionated ktlint_official style. However, there are some problems with this:

  1. The default rules for ktlint should more useful than one person's opinionated style.
  2. The intellij_idea and android_studio styles have become second-class features. Opinionated additions from ktlint_official have crept in, and these styles no longer align with the official style guides. With each new release, an additional rule or two has to be disabled to maintain alignment with the official guidance, and doing so may cause ktlint not to enforce what the official guide does say in some cases.
  3. The principles of the project currently include minimizing configuration options, so it is unclear how long existing workarounds will be available, or if new workarounds will be accepted.

It's true that the original stated vision for ktlint may have included zero configuration in order to prevent "bikeshedding" about lint rules. In recent years, the direction of ktlint has leaned heavily into this, desiring minimal configuration and consistency to the point of determinism, even at the expense of code clarity and readability. However, an opinionated, deterministic linter with no configuration options, ktfmt, already exists for Kotlin. We don't need another one. Also, ktlint has gradually added some configuration options over time due to demand from the community. It's clear that people use ktlint because it is not deterministic and is configurable, so the stated vision and direction of ktlint should be adjusted to align with this.

Therefore, I am proposing the following course correction for ktlint:

If you agree with this proposed course correction, show your support by reacting with :+1:. If you disagree, react with :-1:. If you have a suggestion to improve this plan, please leave a comment.

EDIT: If you look further down in this thread, we have reached a compromise. Configurability will allow users to customize the styling enforced by ktlint, and that will be my focus. The existing code styles will remain as they are so as not to affect indifferent/happy users that are just using the defaults.

eygraber commented 1 week ago

If you agree with this new direction, show your support by reacting with πŸ‘

@meyertime I think it might be more clear to say "If you agree with this course correction", because otherwise it could be read a little ambiguously; new direction could mean the course correction you proposed or the new direction that ktlint is currently headed in.

meyertime commented 1 week ago

Good point. Updated.

shashachu commented 1 week ago

Thanks @eygraber for pointing out this issue in the kotlinlang Slack. I think the fundamental tension is the been lack of general ownership and support of ktlint, which we (Pinterest) should absolutely take responsibility for, as we have 'owned' the project in name only for quite some time.

When we took over ktlint years ago there was a robust community of contributors (myself included) and for various reasons, we all stopped putting time into ktlint.

@paul-dingemans has essentially single-handedly kept the project alive since then, spending what is likely hundreds of hours of his own personal time, and I want to acknowledge his enormous contributions to the project. As a result, it has become a single-maintainer project not really by design, but because he's the one putting in the vast majority of the work into the project.

@meyertime I completely understand and acknowledge the frustration you're feeling; I do want to say in general that 'software by committee' is not a long term way to keep a project healthy, and that likely Paul is trying to strike a proper balance between the general principals of the project and trying to glean what the community at large wants. Let's in general try to avoid veering into the direction of personal attacks.

For #2138 in particular, I would also consider this a regression, as we use this formatting in our own codebase. I think most Android developers would as well.

I would be open to adding another maintainer or two (I suspect Paul will as well), and I would personally like to have someone who works primarily on Android, as that's obviously a huge proportion of the Kotlin userbase.

meyertime commented 1 week ago

Thank you, @shashachu, for your thoughtful response.

I agree that "software by committee" is not good long-term, and that isn't my intention here. I am also trying to gauge what the community wants and find out if many agree with me or if I am an outlier. I have seen some response here and in other forums that suggest that I am not the only one who is frustrated with the direction of ktlint.

Part of the problem is that I disagree with "the general principles of the project". As I described above, I think ktlint diverged from its stated vision in order to meet the needs of the community by becoming configurable and thus less opinionated. Then, Paul started to bring it back to those earlier stated principles. I think it's better to continue to meet the needs of the community and adjust the principles accordingly.

I also think the nature of linters in general is that they naturally reach a state where they are more or less "done" and do the job they're intended to do. At that point, it's just a matter of keeping up with any new language features that get added. I feel like ktlint got to that point before ktlint_official was introduced, which may explain why people stopped contributing. I don't think ktlint_official was born out of a need from the community.

At this point, I'm prepared to contribute as much as I can, but I'm not sure adding another maintainer would help if the maintainers don't agree on the principles. So I'm also trying to gauge if we can strike a balance or if it's time to fork.

shashachu commented 1 week ago

@meyertime Forking in my opinion is a 'last resort' option and would probably be a worse outcome for everyone including the community, so I think we can come to a compromise. I agree with you that 'anti-bikeshedding' has largely been aspirational even from the beginning with ktlint, and it is true that ktfmt exists now as a Kotlin linting alternative and has a very different implementation.

Would codestyles be a 'middle ground' here? Is the sticking point that you want to use ktlint_official due to it being the default? Perhaps it can be renamed ktlint_strict if that's the intention. I agree with you that idea and android should closely align with the recommendations of those groups, so I'd also say we shouldn't have style-creep into those groups that conflict with IDEA and Android. It can also be difficult to have 100% alignment with those styles so we only have to guess about changes, or closely follow the commits going into their plugins.

I'd like to give @paul-dingemans a chance to respond here as well. I'm happy to also chat more real-time in kotlinlang, although I believe time zones might make that more difficult.

meyertime commented 1 week ago

@paul-dingemans I just want to say that none of this was meant as a personal attack. Please understand that it is hard to be critical of the direction ktlint is going without being critical of the person who has been singlehandedly maintaining it. I edited this issue and made an effort to soften the language and focus more on the problems I see with ktlint. I am sorry that my frustration came out with it initially.

@shashachu I will respond to your questions soon. In the meantime, I did try to get into the Kotlinlang Slack yesterday, but wasn't able. Something about not having a jetbrains.com email address. Any ideas?

eygraber commented 1 week ago

I've been using ktlint for a long time. Originally through the detekt ruleset, and more recently directly through the ktlint binary.

I agree with everything that @meyertime said. My list of disabled rules keeps growing, and my requests for bug fixes and new features are summarily dismissed.

I think detekt is a good project to look up to in terms of configuration (as well as many other things). There's very sensible defaults, and the freedom to adapt it to your project as needed (within reason).

I would love to see that level of freedom in ktlint, as well as an easier way to write rules (again following detekt).

@meyertime have you tried http://surveys.jetbrains.com/s3/kotlin-slack-sign-up

meyertime commented 1 week ago

@shashachu

Forking in my opinion is a 'last resort' option and would probably be a worse outcome for everyone including the community, so I think we can come to a compromise.

Agreed!

Would codestyles be a 'middle ground' here? Is the sticking point that you want to use ktlint_official due to it being the default? Perhaps it can be renamed ktlint_strict if that's the intention.

I question the usefulness of including an opinionated, unconfigurable code style, especially when ktfmt exists. But more than that, I believe its inclusion causes more harm than good. For one thing, since there is nothing underpinning ktlint_official, such as an official style guide, there is no basis to decide what its rules should be. It fosters threads that spin in circles and don't go anywhere, because it's just one person's opinion vs. another's. Then, since additional configuration options are to be avoided, the issue can't be resolved that way either. Last but not least, the fact that it's the default and blessed code style causes intellij_idea and android_studio to take a back seat, and they don't get the attention they need. Plus new users are likely to start with the default settings, and when they see wonky results, which is more likely the more opinionated the rules are, they are more likely to open issues and start arguing about it, or just not use ktlint if they don't know about the other styles.

If ktlint_official is kept, then it should at least be made less opinionated, and there should be something underpinning it so that disputes can be resolved. For example, perhaps some combination of the two official style guides (such as, "only rules that appear in both style guides", or "all rules from both guides unless they contradict each other").

I agree with you that idea and android should closely align with the recommendations of those groups, so I'd also say we shouldn't have style-creep into those groups that conflict with IDEA and Android. It can also be difficult to have 100% alignment with those styles so we only have to guess about changes, or closely follow the commits going into their plugins.

I'm glad we agree on this. Getting intellij_idea and android_studio to align with the official style guides would be huge! I could certainly help by identifying the ways in which they don't, opening issues, and potentially contributing fixes. Well, at least for android_studio, since the project I'm leading is an Android app, and that's the code style we're using.

This brings up another issue. Historically, official and android were compatible with IntelliJ's built-in formatter, in addition to trying to follow the official style guides. This was a pain point for @paul-dingemans, and he mentioned it as one of the primary motivators for ktlint_official. It also explains why these were renamed to intellij_idea and android_studio. I think it would have been better to just drop that requirement from official and android and focus only on the official style guides than to introduce a third more opinionated style. Or, alternatively, to have 4 styles: intellij_idea and android_studio match IntelliJ's formatter, while official and android only the official style guides. But that may be a bit much.

Finally, there's the issue of configurability. All of the problems with code styles would be a moot point if the rules were configurable. Then code styles would just be different starting configurations. We wouldn't have to argue about it, we could just configure ktlint to our liking. I know that configurability will require careful thought and plenty of automated tests to prevent regressions, but it has been done successfully and sustainably in other linters. The heavy resistance to configurability is giving us fewer options for resolving issues.

So, in summary, if I were to rank my sticking points:

  1. ~Getting intellij_idea and android_studio aligned with the official style guides~
  2. Embrace configurability (within reason, of course)
  3. ~ktlint_official not gumming up the works~
meyertime commented 1 week ago

@eygraber Thanks for the Slack sign-up link. I filled it out, and I'm waiting for a response.

Also, thanks for the additional context. Full disclosure: I am relatively new to ktlint. Added it to our Android project 13 months ago. So my knowledge of ktlint's history is based on research, not experience. I'm not new to software development, though.

paul-dingemans commented 1 week ago

Forking in my opinion is a 'last resort' option and would probably be a worse outcome for everyone including the community, so I think we can come to a compromise.

Agreed!

Forking the project will not only harm the ktlint community, but all the API Integrations and possibly the Ruleset Providers (like compose-rules) as well. Each of those projects will have to decide what they will do. Follow the original project, or the fork, or both.

I find it questionable that you make it sound that only my personal opinions are leading in this project. Of course, my personal opinion has had great influence on this project. This was not by design or intentional, but a consequence of a community which is not responsive enough to get accurate insights on future developments of the project. I have tried to reach out to the community before making changes. I have raised issues in the Github project and advertised for this in the Slack channel. The amount of responses was negligible. For the investigation regarding formatting the when-statement, I have used LinkedIn to gather feedback which worked surprisingly better. This feedback resulted in implementing a different default choice for formatting the statement.

Previously, ktlint aligned with the official style guides. Now, the default code style, ktlint_official, is opinionated and conflicts with and exceeds the official guides. Since the guides no longer serve as an anchor, this default style ends up reflecting the personal opinion of the maintainer. Issues #2138 and #2423 are good examples of this, because the changes proposed have strong community support, and they are aligned with both official style guides, yet they have been refused.

The code styles android_studio (previously android) and intellij_idea (previously official) already did not align fully with the originating style guides from the moment that I joined the project. Some rules of the Androids Kotlin Style guide leaked into the intellij_idea implementation. Same for rules of the Kotlin Coding Conventions have been leaking into the android_studio implementation. Sometimes they directly conflict with the guideline. Did you know for example that the default indentation of the ident type of android_studio is a tab while the (current) style guide explicitly declares not to do so?

The point that I want to make with above, is that style guides are not more than a guide. On a lot of details they are quite specific. On some points (trailing comma's) they explicitly leave it to the discretion of the developer to pick something reasonable. And, on other topics the guides do not mention anything at all. The kotlin_official code style attempts to fill in the blanks between, and left open by the Androids Kotlin Style guide and Kotlin Coding Conventions.

Occasionally, I have tried to get more clarification about the Kotlin Coding Conventions by submitting an issue to the Jetbrains issue tracker. In issue, Roman Elizarov remarked:

Our Kotlin style guide tries to be as simple as possible. We don't have a goal to exhaustively specify how exactly everything should be named and laid out. We are only making recommendations.

A 'new' language feature like context receivers has still not resulted in an update of the style guide as of today. Should any request for formatting regarding such features be denied as no reference to it is found in the style guides?

I think that the value of ktlint, and especially the ktlint_official code style, is to be more specific than the guidelines as it results in more consistent code throughout a code base. Would you be willing to scrutinize every line of code in ktlint to make it 100% fully compatible with the guidelines? Do you really think that if you would succeed in that, that it makes all ktlint users to be more happy?

Let me clarify this with an example. Androids Kotlin Style guide explicitly defines a maximum line length of 100 characters. The Kotlin Coding Conventions does not specify any rule about it. In ktlint_official code style this has been merged into setting a default of 140 (yes, this is an opinionated value) characters. If android_studio code style would be 100% compatible with the Androids Kotlin Style guide, this would mean that you also need to remove features that allow single strings to pass the max line length.

There will be no requirement that these styles be compatible with the built-in formatter of IntelliJ or Android Studio. At best, these are flawed implementations of the official style guides; the official guides are the standard we will measure by.

I am glad that we agree on this. The ktlint_official code style has dropped that requirement explicitly which was needed to resolve bug reports from the community. As users of the android_studio and intellij_idea code style would still complain about code being formatted differently by ktlint compared to Intellij IDEA formatter, there was no other way than to create a new code style. But dropping this requirement for android_studio and intellij_idea will once more upset users.

The intellij_idea and android_studio styles have become second-class features. Opinionated additions from ktlint_official have crept in, and these styles no longer align with the official style guides. With each new release, an additional rule or two has to be disabled to maintain alignment with the official guidance, and doing so may cause ktlint not to enforce what the official guide does say in some cases.

I disagree with the labeling intellij_idea and android_studio code styles as second-class features. When I write new rules, I am aiming to get them working in all code styles. In case such a rule is an addition on top of Androids Kotlin Style guide and Kotlin Coding Conventions they are enabled by default only in the ktlint_official code style. An opt-in in the other code styles is possible. This is totally inline with your vision that additional rules should not be added to intellij_idea and android_studio if this is not founded in the corresponding style guide.

If somehow functionality which is intended for ktlint_official has crept into code formatted with intellij_idea or android_studio that may be marked a bug. However, if you use intellij_idea or android_studio code style, and you have enabled a rule which by default is only enabled in ktlint_official than this should not be considered as a bug as the user explicitly decides to leave the path of one of the predefined code styles.

The principles of the project currently include minimizing configuration options, so it is unclear how long existing workarounds will be available, or if new workarounds will be accepted.

Minimizing the number of configuration options is born out of necessity. Lots of people just seem to assume that each of their favoured enhancements have to be built into the project. Often people than suggest to put the functionality behind a configuration setting. Although that can be helpful, it has the downside of introducing a lot of complexity. Not because of adding some conditional logic, but due to the (combinatorial) explosion of execution paths that result as of it. The least harmful configuration options, are the options which are related to numeric values like tab size, and number of parameters in a function before to start wrapping), and I have no real objection to those. Most harmful configuration options are the enumerations like code style, and function body expression wrapping.

Removing existing configuration settings, and changing default settings (for example the indent style in android_studio) is nearly impossible. It will upset lots of developers, and resulting in more maintenance (handling issues) compared to the benefits. Introduction of new configuration options is a possibility, but is not one that is taken lightly. In the same way, you do not have to fear that I will ever suggest to drop the code styles android_studio or intellij_idea entirely.

Since the guides no longer serve as an anchor, this default style ends up reflecting the personal opinion of the maintainer. Issues #2138 and #2423 are good examples of this, because the changes proposed have strong community support, and they are aligned with both official style guides, yet they have been refused.

It is true that #2138 and #2423 have an unusual high number of participants that actively engage the discussion, and like or dislike comments. Even when every like or dislike of a comment would come from a unique visitor, those threads would count for 150 users. But it is more likely that some of those users have (dis)liked multiple comments resulting in less unique users that are voicing a complaint.

Ktlint has tens of thousands of users. The vast majority of those users are invisible from the viewpoint of the project. We have no metrics about the usage patterns. We don't know how many users use a specific code style. We don't know which rules are disabled. We don't know the number of users per version of the ktlint rule sets. This makes decision-making hard. Even with input of some active users the outcome of any decision is still opinionated.

What I do know from the ktlint-intelli-plugin version 0.22 (e.g. ktlint 1.2.1) is that it has been downloaded by 17,799 developers as of today. The '0.23.0' version of the plugin has been downloaded 4,659 developers within one week after its release. I expect that the total number of ktlint users (either via Ktlint CLI, the gradle and maven plugins, Detekt, Spotless) is a multiple of the number of the users of ktlint-intellij-plugin.

Active users in the ktlint project, including the Slack channel, typically have a question, an enhancement request, or they report a bug. They have an apparent reason to become active. Only some of them stay active for a longer period of time. The active users however miss a representation of users that are either satisfied with the project, or don't dare to raise their voice in public project regardless whether they like or dislike the project. How can anyone claim that they represent the entire ktlint community, and therefore should be listened to? I cannot claim to represent all ktlint users that are not active contributors or readers of this project issue. But neither can you claim that the active users in #2138 and #2423 do represent all users that wants this to be resolved in the way you propose.

As maintainer and guardian of the project it is my responsibility to balance the interests of all users as good as possible. I am trying to balance requests from the Android community, the non-Android Kotlin community, the API Consumers and Ruleset Providers. This means that android_studio code style is based on Androids Kotlin Style guide, and intellij_idea is based on Kotlin Coding Conventions. The ktlint_official code style combines the best of both guidelines, when necessary it extends those guidelines to provide a consistent code formatting experience.

Each user that raises or supports an issue like #2138 and #2423 is important, and should be listened to within reasonable bounds. If a request is not followed up to your satisfaction, the next step is to actively contribute a change by raising a pull request. We can negotiate about which solutions are acceptable to both of us.

@shashachu wrote:

I would be open to adding another maintainer or two (I suspect Paul will as well), and I would personally like to have someone who works primarily on Android, as that's obviously a huge proportion of the Kotlin userbase.

I would prefer so select maintainer only after there is a proven track record on the project. This would require regular (code) contributions, performing code reviews, participate actively in discussions on the issue tracker, and responding on questions on Slack. I am open to mentoring a potential new maintainer.

ephemient commented 1 week ago

Forking the project will not only harm the ktlint community, but all the API Integrations and possibly the Ruleset Providers (like compose-rules) as well. Each of those projects will have to decide what they will do. Follow the original project, or the fork, or both.

As somebody who maintains a custom Gradle integration for a private project, I'll just follow whichever one actually works for us. Sadly I have to say that there's been no value in the additions to the ktlint style for us.

eygraber commented 1 week ago

@paul-dingemans my conclusion from reading through your comments is that there are two options for moving forward:

  1. Stay the course, and deal with the inevitable splintering and crumbling of the community
  2. Embrace configuration

In this case configuration would decrease the current bike shedding problem:

  1. @paul-dingemans wants to make a change
  2. No one is interested in discussing it (I'll leave my opinions about getting feedback from LinkedIn to myself for now)
  3. @paul-dingemans decides how it will work
  4. Community doesn't like the decision
  5. Community is "unable to convince @paul-dingemans that he made the wrong choice"
  6. More discussion and anger about how another rule needs to be disabled and the direction of ktlint, etc...

The fact is you're going to have a hard time getting quality feedback for formatting features, because no one cares about it until it's breaking their project. Aside from the fact that feedback is just opinion anyways, and there will never be consensus on opinion based matters. There isn't even broad consensus about the Kotlin style guide's inhospitality to variance.

The only way to deal with that is by providing broader configuration options. Will it add complexity? Probably. Is it unattainable because of that? No, there are plenty of formatters/linters out there that work just fine while allowing a high level of configuration. Detekt is a prime example in the Kotlin community. They pick sane defaults, try to anticipate variant needs up front, and are open to adding configuration to address them as the community needs it.

wakingrufus commented 1 week ago

As the primary maintainer of https://github.com/JLLeitschuh/ktlint-gradle these days, I don't really have a strong opinion about ktlint_official vs intellij_idea. I use intellij_idea in my projects, but a single line in my .editorconfig to enable that is not something that bothers me at all. But that brings up one thing I want to point out: the trend toward using .editorconfig as the primary way to configure ktlint has reduced the complexity of using/integrating with ktlint, as well as made it easier to resolve those IDE/ktlint differences, when they do occur, without multiple sets of configurations. So kudos to @paul-dingemans for having that vision and moving in that positive direction. If a more "configurable" ktlint is the future direction, I hope to see it be implemented in the same way, and perhaps with coordination with JetBrains to develop shared .editorconfig properties that ktlint and ij can both use in a consistent way (meaning less ij or ktlint specific properties)

wakingrufus commented 1 week ago

I had one more thing to add: if governance of ktlint is being reconsidered from Pinterest's side, as @shashachu implied was possible, I'd suggest combining ktlint, klint-gradle, ktlint-intellij-plugin into a single GitHub org, then applying to become a member of CommonHaus (https://www.commonhaus.org/). Commonhaus has established some patterns and systems to help with community-project feedback loops, while not requiring projects adopt any specific pre-determined processes.

skylerreimer commented 1 week ago

I'd like to add a comment from the perspective of an indifferent/happy user.

I've used ktlint across several android related projects over the years and every so often it'll format my code in a way that is not expected. My current project has always been on the latest versions of the library. I usually shrug off the unexpected formatting and move on. I suspect a large chunk of users fall into this bucket. I would personally rather have a standardized rule set across all kotlin codebases than nitpick or add custom rules. It makes everything easier to read when we all have the same format. For these reasons I don't agree with the statement that a single maintainer will cause the community to crumble.

That said, it seems like Pinterest is loosely maintaining this repo and the primary maintainer doesn't even work there. This is a big and important project for many organizations. Grabbing an android focused maintainer and migrating this project (and related plugins) to a single github org seems to be a viable option if Pinterest isn't as committed as they once were to maintaining ktlint.

meyertime commented 1 week ago

@paul-dingemans

I find it questionable that you make it sound that only my personal opinions are leading in this project. Of course, my personal opinion has had great influence on this project. This was not by design or intentional, but a consequence of a community which is not responsive enough to get accurate insights on future developments of the project.

That's fair. I don't think that it's only your personal opinions, or that you are necessarily doing it intentionally. But as you even admitted, a lot of times it ends up being your personal opinion that decides things anyway. That puts the unreasonable burden on the community to change your mind rather than point to an established guideline to settle things. Whether you can change a person's mind often has more to do with how stubborn that person is than how convincing your argument is.

Perhaps engaging with the community and getting more feedback is something that Pinterest could help with.

On some points (trailing comma's) they explicitly leave it to the discretion of the developer to pick something reasonable.

In cases like these, I think there is a super strong argument for a configuration option. If the language designer saw fit to give developers a choice explicitly in the official guide, then it's reasonable that ktlint should too. Both style guides explicitly give the option to put annotations without parameters on the same line as the declaration, yet you rejected the idea of having a configuration option for it in #2138.

And, on other topics the guides do not mention anything at all.

In that case, there may very well be a good argument for a configuration option. If the language designer wasn't willing to say that "everyone should format their code this way", then neither should ktlint, unless the user specifically configures it.

A 'new' language feature like context receivers has still not resulted in an update of the style guide as of today. Should any request for formatting regarding such features be denied as no reference to it is found in the style guides?

Basically, yes. New language features need to be used for a while to see which formatting would be best. Plus the syntax of the feature may be subject to change which could break the rule. Perhaps an experimental configurable rule could be added for those who want to enforce a particular style, but it should not be enabled by default. It definitely should be configurable, though, to allow different users to experiment until a consistent pattern develops. Context receivers are still experimental and have to be explicitly enabled by compiler option, and JetBrains still hasn't updated their Kotlin documentation even to acknowledge the existence of context receivers, so I'm not surprised the style guide hasn't been updated yet either.

I think that the value of ktlint, and especially the ktlint_official code style, is to be more specific than the guidelines as it results in more consistent code throughout a code base.

I agree and disagree. You're right in that ktlint would not be as valuable if it couldn't be any more specific than the official style guides. What I disagree with is that it can only exceed the style guides in a specific opinionated unconfigurable way, and especially when that way is decided by one person's opinion. Yes, we can disable rules we don't like, but in most cases, we can't configure them to behave differently. This narrows the potential usefulness of ktlint to those users whose opinions happen to agree with yours.

For the record, I would feel this way no matter whose opinion the specific opinionated unconfigurable way reflected. Even if it were my opinions, it might make ktlint more useful for me, but not for too many others.

The ktlint_official code style combines the best of both guidelines, when necessary it extends those guidelines to provide a consistent code formatting experience.

Ok. So that first part about combining both style guides is good. That originally appealed to me because our team was writing both the backend and Android frontend in Kotlin, and I liked the idea of having one unified code style to rule them all. That's why I originally opted into ktlint_official when I added ktlint to our project. When I ran into the annotation wrapping issue after updating ktlint, I checked the style guides and found that they both agreed and saw fit to open #2138 to fix the issue with ktlint_official. I expected it to be an easy sell given the emphasis on alignment with the official style guides placed by the official ktlint documentation. In fact, the contributing guidelines still say this:

ktlint only provides rules that enforce the Kotlin coding conventions or Android Kotlin style guide. If your change is more opinionated than please file an issue first so that it can be discussed amongst the community. Rules which are too opinionated might be better published as a custom rule set.

However, it's that "best" part that really irritates me. (As in, "the best of both guidelines", which you also say in the ktlint code styles documentation.) In #2138, you dismissed my suggestion, not because it wasn't "better", but because 'better is subjective', implying that it is not a valid basis to decide what ktlint_official should do. Yet, here you state it as your basis for deciding what ktlint_official should do. Talk about a double standard!

I don't agree that "better" is always subjective, but in any case, your description of what ktlint_official should be is vague and ambiguous when it comes to how the guidelines should be combined or how they should or shouldn't be extended. In practice, you have thrown out what the guidelines say when it suits you and invoked this vague description to justify doing whatever your personal opinion dictates. I am calling for there to be an objective basis which has community support that can be used to decide things and that you be held accountable to it equally. I threw out one possibility above to start gauging community sentiment. I'm not set on it, but something needs to change.

Would you be willing to scrutinize every line of code in ktlint to make it 100% fully compatible with the guidelines? Do you really think that if you would succeed in that, that it makes all ktlint users to be more happy?

That is exactly what I would do if I forked ktlint, and then we would find out how many users it makes happier, or new users it garners. Actually, I would refactor all the rules to make them configurable in the process. Then the code styles would be nothing more than default configuration that matches the official style guides.

You were willing to make breaking changes that required people to change their configuration for the version 1.0 release. We can make a version 2.0 where the defaults match the style guides and configuration options are available for people who still want it the old way. It would actually help ktlint users see how their configuration diverges from the official guidance.

Removing existing configuration settings, and changing default settings (for example the indent style in android_studio) is nearly impossible. It will upset lots of developers, and resulting in more maintenance (handling issues) compared to the benefits.

The really frustrating part about this is that you seem so concerned about hypothetically upsetting a lot of users who up until now have been silent, yet you're unwilling to listen to the users who are already actually upset and telling you why. On top of that, we're suggesting plenty of options that would not upset other users.

It's not impossible at all. You did it in version 1.0. The default code style was changed to a brand new one, and the existing ones were renamed, so literally everyone had to make a change to their .editorconfig or face a slew of lint violations. But as developers, we know that when we upgrade a dependency, there may be breaking changes, especially when that major version gets bumped. In that case, we check the release notes or upgrade guide. It's business as usual, standard operating procedure. If there's a developer out there who can't handle that, then they should probably look for a new line of work. A lot of major software vendors have gotten smart and moved to rapid release cycles where major versions are released yearly or even more frequently. That lets them shed the old cruft and move forward.

Plus we're talking about making ktlint more configurable and thus more flexible and able to handle a wider variety of requirements. That can only make more people happy.

If a request is not followed up to your satisfaction, the next step is to actively contribute a change by raising a pull request. We can negotiate about which solutions are acceptable to both of us.

As I mentioned in #2138, you rejected our ideas in that issue, so why would we bother working on a pull request? What makes us think you would actually merge it? Nowhere in that thread did you even imply that you would consider merging a PR that changes the behavior, adds a configuration option, or any other solution. So I have to implement a solution and submit a pull request for it first before you'll tell me if it's an acceptable solution? We've been trying to negotiate solutions right there in that issue. And you wonder why no one is contributing?

Tell me that if I submitted a PR that added a configuration option to annotation wrapping in order to resolve #2138, and it met code quality standards, etc., etc., you would merge it, and I'll do it. I'll even refactor that rule to make it fully configurable in the process.

Minimizing the number of configuration options is born out of necessity. Lots of people just seem to assume that each of their favoured enhancements have to be built into the project. Often people than suggest to put the functionality behind a configuration setting. Although that can be helpful, it has the downside of introducing a lot of complexity. Not because of adding some conditional logic, but due to the (combinatorial) explosion of execution paths that result as of it.

I've been writing code for 25 years. I'm well aware of this. It looks like you've been writing code longer than that, so I'm sure you know by now that the way to manage the complexity is to break the code into small, simple units that can be tested independently. Small things with well-defined interfaces between them. Then you don't have to test every combinatorial execution path, which is indeed unrealistic for any sizable project. As brought out, other projects have done configurable linting successfully, so it really isn't necessary to minimize configuration.

And this isn't just my "favoured enhancement". (Speaking again of #2138) I'm sure you might get a lot of outliers that come in and request something, you say no, and don't hear anything after that. However, when person after person keeps chiming in and agreeing, and nobody but you is defending the way ktlint currently formats it, and on top of that, both official style guides agree with me, that's another story. No, I can't say I represent the entire ktlint community, but based on what I've seen, I do believe I represent a significant chunk of it. I wouldn't be speaking up like this if it weren't for the continual response to #2138.

Now it's true that #2138 is a big deal. In fact, it's a deal-breaker for me. There's no way I would let that rule run in my code the way it is. The formatting is awful. But it's not just about #2138. Your response to things like #2138 sets a precedent for what we have come to expect from this project. I have no confidence that if I open another issue, I will get a reasonable response, or if I try to contribute an improvement that makes ktlint more flexible and configurable, it will see the light of day. Why bother? And I can't help feeling that's probably why others aren't bothering either. That's a problem, and that's why I appeal to Pinterest to take more ownership, find a way to engage with the community and find out what it really wants, and lay down a clear roadmap based on that.

meyertime commented 1 week ago

In this case configuration would decrease the current bike shedding problem:

  1. @paul-dingemans wants to make a change
  2. No one is interested in discussing it (I'll leave my opinions about getting feedback from LinkedIn to myself for now)
  3. @paul-dingemans decides how it will work
  4. Community doesn't like the decision
  5. Community is "unable to convince @paul-dingemans that he made the wrong choice"
  6. More discussion and anger about how another rule needs to be disabled and the direction of ktlint, etc...

@eygraber Way to summarize it! That hits the nail on the head. Totally agree on configurability. If we do end up forking, we should work together.

meyertime commented 1 week ago

I've updated my sticking points:

  1. Embrace configurability

That's it.

Will I still think it's silly to have defaults that reflect one person's opinion? Yeah. Would I still like to have code styles that align with the official style guides? Sure. But with configurability, I can get around all of that, and so can anyone that disagrees with Paul's opinions. The indifferent/happy users can go about their business unaffected. Even Paul won't have to bikeshed anymore; he can just point people to the relevant configuration option. Everybody wins.

npouliquen commented 1 week ago

Basic rules that follow official style guides + configuration around non-standardized rules or things left up to choice by the official style guides seems like a great way to handle a widely consumed linter/formatter. Code styling is an interesting issue. There are things that we would all agree are objectively wrong, like having a different level of indentation on every line. Somewhere between that and trailing commas, there is a threshold which separates formatting that's obviously good and formatting where there isn't extremely wide consensus.

Because of this, I'm in favor of following the conventions set by the official style guide and leaving everything else up to choice of the consumers. The boundaries of a developers preferences for code styling are usually subjective. You can make arguments for or against trailing commas, tabs vs spaces, level of indentation, spaces before/after certain symbols. Neither direction is objectively good or bad. One code style/rule may fit a certain project and/or development team, while another style/rule suits another project. IMO, this is an OK thing. Not everyone has the same favorite color or likes the same music. Not all Kotlin code bases need to conform to the exact same standards for formatting.

Adding more configurability would also enable folks to share their configuration with others. This is something that developers clearly enjoy doing with other tools. Why not with Ktlint? You could link to a page with popular configs from the README so new users could explore possibilities and take ideas from multiple configs. You could even have a default config file that is bundled with the package that says something like,

You can configure this file if you don't like all these defaults. Check here for documentation about how what rules impact formatting. Check here for examples of shared configs from other users.

I can sympathize a lot with the difficulty of maintaining a very widely used package like this, especially a package which, by its very nature, leads to a lot of disagreement and debate. It's not an easy job. People just expect it to work, and only really speak up if they're upset. All that said, I broadly agree with most of the points in this issue. I hope we can all come to a happy resolution.

paul-dingemans commented 1 week ago

@meyertime wrote:

Whether you can change a person's mind often has more to do with how stubborn that person is than how convincing your argument is.

This comment really resonates with me. I have to admit that at times I can be stubborn when I am not feeling convinced. It is time for me to change my perspective.

Ktlint is the first open source project I joined. I wanted to contribute back to the community, learn more about kotlin, and learn how to collaborate in an open source project. When I started contributing to the project, it was not in a great shape. Around 150 issues were open, some of which for a very long time. Due to circumstances at that time, the maintainers were not very active. After a few contributions, I have indicated that I was willing to make a long term commitment to contribute to the project, but only if I would become a maintainer. I did not anticipate that it would turn out that would become the only active maintainer. This is not what I wanted. I had no chance to reflect and discuss ideas before implementing them. Code was not being reviewed. All in all, I dare to say that I have implemented great features in Ktlint, and with that ensured that the project became more maintainable. I have made mistakes and misjudgements as well, especially all the breaking changes were painful for the API Consumers. With help of other maintainers and contributors, this could have been achieved in a better way.

Luckily we all agree that it is not in the interest of the ktlint community to fork the project. We should not compete about our users, API Consumers, Rule Providers, brand names, etc. I see lots of good suggestions by @meyertime regarding vision how this could be approached. Such a big vision is hard to be achieved by a single person.

As I still want to be part of a community that is collaborating on ktlint, I propose that we conduct an experiment to add a configurable solution for #2138 in current release train 1.x.

I propose following:

Although I guarantee that I will keep my word, I can imagine that you cannot (yet) trust that. The risk mitagition is quite simple though. In case our collaboration is not successful, you can still fork the project and apply the pull request in the fork. Next to this, Pinterest is still the owner of the project, and as of that they can always overrule me. In such a case somebody else can become maintainer, and keep on serving the community via this project.

My preferred outcome is that we succesfully collaborate on this. After the experiment we should assign at least add one new maintainer, and start on planning to migrate all of ktlint.

meyertime commented 1 week ago

@paul-dingemans I really appreciate this. And thank you for the additional context. I think what you've proposed is totally reasonable.

You have a lot of experience with this code base, so I will definitely keep you in the loop during the process, and would appreciate your insights. I know I've been a little disruptive lately, but I'm not normally that kind of person. I went way out of my comfort zone here. I would love to collaborate.

This is a big and important project for many organizations.

I'd like to offer a little context as well. I'm a Lead Engineer at Target responsible for leading development of the "Pickup" Android app used by Target team members to fulfill pickup/drive-up orders in the store. I'm also in a position to recommend a linting tool and configuration to other teams in the organization. Though we've been using ktlint, I've held back from promoting it because of all the rules we've had to disable on our project. But our options are limited. detekt doesn't do much formatting and doesn't support auto-correction. ktfmt is way too opinionated. So ktlint is the only option. That's why I tried to see if we can make ktlint better for our needs (and the needs of other organizations too).

I will work on this. But it may take me some time. I'm hoping since we use ktlint in our project, I can justify spending a little work time on it, and maybe even get some other Target engineers to help.

For the record, I'm not necessarily trying to become a maintainer. While I have a lot of professional experience, my open source contributions have been here and there as needed for work up until now. As long as the project is going in a good direction, I'm happy just being a contributor. But if there's a need, then I'm not opposed to the idea. I benefit a lot from open source software, so it's only fair that I give back.

I think detekt is a good project to look up to in terms of configuration (as well as many other things). There's very sensible defaults, and the freedom to adapt it to your project as needed (within reason).

I would love to see that level of freedom in ktlint, https://github.com/pinterest/ktlint/issues/1984 (again following detekt).

@eygraber Do you have any specific ideas along these lines and how it would apply in the ktlint code?

I won't make breaking changes to the API, of course, but there may be patterns that could be used to structure the rule code internally.

eygraber commented 1 week ago

Do you have any specific ideas along these lines and how it would apply in the ktlint code?

For making it easier to write rules, @paul-dingemans already made an attempt a while ago, but there were performance issues so it was abandoned. I've always meant to look into but it never made it to the top of my pqueue.

For the configuration, check out https://detekt.dev/docs/rules/style for examples of what they have available.

shashachu commented 1 week ago

Hi all, I'm encouraged by the direction this thread is taking. Thanks @paul-dingemans, @meyertime, and @eygraber for engaging with each other in a constructive manner.

In regards for long-term ownership of this project, I'm chatting with some folks internally on Monday to talk through our options. My personal goal and certainly Pinterest's goal is to keep this project healthy and active, whatever form that may take. I will share updates as I have them.

paul-dingemans commented 1 week ago

@meyertime wrote:

For the record, I'm not necessarily trying to become a maintainer. While I have a lot of professional experience, my open source contributions have been here and there as needed for work up until now. As long as the project is going in a good direction, I'm happy just being a contributor. But if there's a need, then I'm not opposed to the idea. I benefit a lot from open source software, so it's only fair that I give back.

The vision you have presented before is not something which simply can be contributed. You've envisoned big changes to ktlint. I have agreed to support the change to resolve #2138. But please do not expect me to contribute a lot of code and time on implementing the changes for now.

Also, I want to stress that I do not want to remain as single active maintainer if ktlint is changed as you have proposed. This change really asks for ownership of a person that is going to drive the change. I do not want to become the single owner, and single responsible person, for resolving issues and feature requests once the solution for #2138 has been implemented.

You have claimed before that you were willing to fork the repository to get your idea implemented. If you would have forked the project, you would have become a maintainer as well. Please consider carefully whether you are really up for a longer term commitment to this project to get your vision realized.

meyertime commented 1 week ago

Ok, well that answers that question. :sweat_smile: Yes, I will drive the change to make ktlint more configurable (assuming this experiment goes well as planned) and support it as a co-maintainer, responding to issues, etc.

I said what I did because I didn't want you to think I was trying to take over in a hostile sense. Forking would have been a last resort, and yes, I would have been fully responsible for it then. So I'm definitely willing to share the burden with you.

meyertime commented 1 week ago

In regards for long-term ownership of this project, I'm chatting with some folks internally on Monday to talk through our options. My personal goal and certainly Pinterest's goal is to keep this project healthy and active, whatever form that may take. I will share updates as I have them.

@shashachu I still think it would be good if Pinterest could help when it comes to getting good feedback from the community. I'm sure I will run into the same problem as Paul eventually. I am focusing on making the one rule configurable initially (#2138), but then there will be the question of what else should be configurable and what those options should look like, and I won't want to make those decisions unilaterally.

As it has been brought out before, people tend not to pay attention to ktlint until something goes wrong, so the feedback tends to be biased and not necessarily representative of the entire community. I don't know what the answer is to that, but perhaps Pinterest as an organization would have more resources to solve it than we as individuals do.

Thanks again for your attention to this.

eygraber commented 1 week ago

I think a "committee" might help here, where there's a group of people who are available to discuss rules, configuration, etc... That will hopefully solve the issue of no one contributing to the conversation until something goes wrong, as well as not requiring anyone on the committee to be active code contributors (if they don't want to be).

I think it would be nice if one+ of the detekt maintainers would agree to be on the "committee" since they have experience and expertise with these kind of things (cc @BraisGabin).

meyertime commented 1 week ago

I like it. Though we just talked about not wanting to do "software by committee". :laughing: But we don't want design by dictator either. I think a balance is good. The leaders provide a unifying vision and ultimately decide, but with input from a committee of users to make sure it meets the needs of the larger community.

eygraber commented 1 week ago

That's why I put "committee" in quotes πŸ˜…

I guess it's more people who are dedicating themselves to be available for discussion.

wakingrufus commented 1 week ago

I think we have had very productive collaboration between ktlint and ktlint-gradle especially regarding API changes that might effect integration. I think @paul-dingemans has been very proactive and open in this regard. But this has mostly been via informal slack conversations on the kotlin slack. Maybe instead of "committee", we just need a dedicated space to have these discussions in a place that is easy for interested parties to find. Maybe the kotlin slack is that place or maybe there is something better.

eygraber commented 1 week ago

I think those conversations can and should happen in the open (Kotlin slack, etc...). The idea of a "committee" is only that there are people dedicated to responding.

wakingrufus commented 1 week ago

I think those conversations can and should happen in the open (Kotlin slack, etc...). The idea of a "committee" is only that there are people dedicated to responding.

Yes, in the open of course, but maybe there is a way to make it easier to discover, or to gather feedback, such as something like this: https://www.commonhaus.org/bylaws/decision-making.html

eygraber commented 1 week ago

I think the reality of the project is that at the end of the day, most users don't want to get involved in nitty gritty discussions about how their formatter works. So making it easier to discover or gather feedback might not matter as much because of that.

Personally, making formal rules seems like a bit much to me, but I'd be interested in hearing what others think.

skylerreimer commented 1 week ago

Just looking around at kotlin usage internal to Amazon, it seems like most teams set their ktlint version and never thought about it again. There's also almost zero people asking about ktlint in our internal support channels. I think the hypothesis that most people don't care about their linter after they set it up is true.

A strict feedback loop might make it hard to make changes if we think people are not interested in giving feedback.

eygraber commented 1 week ago

I think the hypothesis that most people don't care about their linter after they set it up is true.

Until it does something they really don't like πŸ˜‡

npouliquen commented 1 week ago

Just looking around at kotlin usage internal to Amazon, it seems like most teams set their ktlint version and never thought about it again. There's also almost zero people asking about ktlint in our internal support channels. I think the hypothesis that most people don't care about their linter after they set it up is true.

FWIW, I'm in this boat. I'm at Amazon and use Kotlin. I was trying to upgrade my ktlint dependency a few days back, which brought me to #2138, as I was very unhappy with the result of that formatting. I agree with your sentiment. People just expect their linter/formatter to work the way they want. It isn't something that is top of mind until the output is something they greatly dislike. It's the only reason I came to this repo.

Gathering feedback is probably pretty hard for a project like this. I also don't know how important it is. If the road Ktlint is headed down is to offer more configuration, feedback becomes less important. Ideally, if folks don't like what the default behavior is, they can just change the configuration and it is back to what they like.

BraisGabin commented 1 week ago

I think it would be nice if one+ of the detekt maintainers would agree to be on the "committee" since they have experience and expertise with these kind of things (cc @BraisGabin).

I don't have the time for something like that, I can't give my long term commitment.

But I'm nearly always on at Slack. If someone thinks that what we have on detekt could help ktlint I'm open to answer DMs or any @me on public chats about anything related with this.

paul-dingemans commented 1 week ago

We have had fruitfull discussions here. But it is becoming a little messy with different topics being discussed. I have opened Github Discussion where we split the discussions per topic. Please keep on contributing via the discussion sections.