Changing public
folder in packages and enforce_privacy
mechanism
#219
-
ProblemOne thing that is bothering me for a while is the way The problem is that now this One of the tenant design decision of Packwerk is that it will respect and embrace the Rails philosophy, not go against that, and in this specific feature, it is going against an important tenant of what makes a Rails application a Rails application. Proposals1. Create a mechanism to export constantsIt will consist in allowing developers to call a method to expose a constant to other packages. The Packwerk parser will look for exported constants, keep track of them and which package it is part of, and in a privacy check, will lookup in that collection to see if any violation is being made. class SomePublicEntrypoint
end
export SomePublicEntrypoint This will have the upside of making constants private by default, like proposed in #97. This proposal will require that Packwerk define some kind of runtime component (which will be required by the packages and application). 1.1 Alternative implementationUse comments instead of runtime methods (since we don't have any usage for the runtime aspect of that method). This will remove the need for the runtime component. # @export SomePublicEntrypoint
class SomePublicEntrypoint
end 2. Move the
|
Beta Was this translation helpful? Give feedback.
Replies: 12 comments 21 replies
-
cc @mclark as well |
Beta Was this translation helpful? Give feedback.
-
Thank you for this really thoughtful and interesting analysis! I'm collecting my thoughts and chatting more with my team, and I see a ton of opportunity here! I'll follow up with more thoughts soon, and I'd personally be excited to help solve this problem you're facing. |
Beta Was this translation helpful? Give feedback.
-
I am adding this as a placeholder comment for folks to add a 👍 if they use privacy protections today |
Beta Was this translation helpful? Give feedback.
-
I love option 2 for a variety of reasons, most importantly maybe that it elevates work on a public API in visibility. The reason I have been ok with what we have today is that I actually believe that the investment in a public API shouldn't just be making certain constants public (for many packages). Rather, I believe there should be a deliberate investment in designing the API for the use-cases of other parts of the app. |
Beta Was this translation helpful? Give feedback.
-
I'd explicitly vote against 4 -- even if some changes made to eliminate a privacy violation are suboptimal (just moving files around, etc.), it still provides a mechanism to be more explicit about a package's public API, and we've found value in that even before intentionally redesigning that public API. It's a core part of Packwerk's value proposition for us. We're currently working around the junk drawer problem by defining subpackages -- we'll have a top-level (e.g.) UserService pack, then one in user_service/app/models (with public_path set to
This doesn't completely address the issue, though, since it'd still be exposing all the models (in the case above) as part of the subpackage's public API 🤔 |
Beta Was this translation helpful? Give feedback.
-
This sounds to me like a cultural issue.
The former is a great reason to embrace Packwerk and it also helps reduce the cognitive load for new starters, but if that's all you're trying to achieve, then I agree that In our case though, we also wanted to take this opportunity to apply Hexagonal Architecture patterns inside our application and decouple the different packages to reduce the entanglement in our application. But if the I love this image from the Packwerk USAGE doc, because I think it illustrates very clearly the ultimate objective of using Packwerk to achieve a healthier codebase: |
Beta Was this translation helpful? Give feedback.
-
Caveat: I designed the first versions of packwerk, but I am not currently using it. You may treat my opinions as historical context. OriginsPackwerk was first intended to be purely a dependency management tool. We added privacy enforcement to drive adoption, because that was what people were asking for. In the metaphor of carrots and sticks, privacy is sugar. It's easy to understand has broad appeal, but it may not actually be good for you. There are a lot of intricacies to making things private and public within a Rails application that can be, even excluding cultural issues, difficult to resolve. However, I've been convinced back then and still am that dependency management is by far the more useful mechanism. It is a lot less likely to lead to bad design decisions. Also, it is much easier to establish conventions for public/private (e.g. in naming) than it is for dependencies. To emphasize, I do think the value proposition of packwerk is in dependencies, and privacy as an option is a nice cherry on top. My PreferenceFor the above reasons, my preferred solution is Explicitly Exporting ConstantsI think explicitly exporting constants would only exacerbate the problem lined out by Rafael. In my opinion, having all kinds of things in the public folder is a smell. If we intentionally design and implement public interfaces, things in the public folder will be dedicated to that purpose. So there shouldn't be a broad array of types of things in there. Unless we're talking about cross-package inheritance - but I think that warrants a separate discussion. If we change the mechanism from a public folder to "export" annotations, we will make the public interface a lot more implicit. IMO it will actually make it easier to say "I want to use this thing but I can't, so I'll just export it". Someone trying to judge the quality of the interface will not easily see that there are different kinds of things in the interface, because there isn't one place to look at. There also isn't one place to look at for people that want to use a package. Cultural ProblemsIt was brought up that the problem discussed here is a cultural one. IMO there are no purely cultural problems in software development, just as there are no purely technical ones above a certain scale. Yes, there is a cultural component here. But tools shape culture (and vice versa), and packwerk was very much designed for that purpose. Part of this discussion should be how we want the privacy mechanism to shape people's behavior. |
Beta Was this translation helpful? Give feedback.
-
This has been a really interesting and thoughtful discussion, thank you everyone for participating and sharing your thoughts! I have a proposal that I think might be satisfying based on what I'm reading in this thread. What if, as a first step, we support a plugin architecture to packwerk checkers and move privacy protection, with its behavior unchanged, into a gem. This would require no behavioral change for consumers – they simply bump packwerk to 3.0 and add the gem to their gem file, at which point packwerk should continue to operate as normal. This does a couple of things:
How do people feel about having packwerk work with a plugin system and pulling privacy enforcement into a gem and iterating on its behavior independently? |
Beta Was this translation helpful? Give feedback.
-
Separately, I wanted to share a bit more what we discussed as a team at Gusto. Just to clarify -- I think that this is independent of the decision to move it into a plugin which is why this is a separate thread. At Gusto we are really invested in the privacy checking component of packwerk, and want to continue to improve it. We feel like there are multiple separate, independent problems we are trying to solve and requirements we are trying to enforce:
I like this framing because it decouples the problems from the solutions (annotations, public folder, etc.). Are there other problems/requirements that I'm missing here? If not, what we generally landed on as a team was the use of a magic comment at the top of a file to denote a file as public. Here's how it does or can solve the problems above:
|
Beta Was this translation helpful? Give feedback.
-
I've created three PRs to move towards extracting privacy out as a plugin: Once these land, I think I'll have enough to extract it out as a gem plugin, then I can put up a PR in At that point if we want, we could choose to remove it from |
Beta Was this translation helpful? Give feedback.
-
Hey folks – wanted to share that this work is wrapping up. You can see the PR here that finishes up this extraction: #247 The packwerk privacy extension is being extracted here rubyatscale/packwerk-extensions#1 Let's move discussion about improvements to the privacy checker to here: rubyatscale/packwerk-extensions#2 |
Beta Was this translation helpful? Give feedback.
-
This is an old issue - but one that I've pointed people to recently for reasons to reconsider privacy enforcment. If you still want to pursue it and the folder structure of the second proposal is interesting - you can achieve it with a snippet like this: # config/initializers/public_zeitwerk_components.rb
# assuming packages are stored in a components/ directory
Rails.application.config.before_initialize do
Dir.glob(Rails.root.join("components/*/public/*/").each do |dir|
Rails.autoloaders.main.push_dir(dir)
end
end |
Beta Was this translation helpful? Give feedback.
Hey folks – wanted to share that this work is wrapping up. You can see the PR here that finishes up this extraction: #247
The packwerk privacy extension is being extracted here rubyatscale/packwerk-extensions#1
Let's move discussion about improvements to the privacy checker to here: rubyatscale/packwerk-extensions#2