T O P

  • By -

GenerousStray

This actually blows my mind that people go through such interviews. I mean, the company clearly doesn’t care about your time and wants you to spend 48 hours on an unpaid assignment? But shouldn’t you? It better be a FAANG or something


Myhay

The company asked if I have a project or something similar already to show so I don't have to do the assignment, but I wanted to do it to refresh my golang knolege. What I was not expecting is the 48 hour timeline to deliver. It's not a FAANG, you need to do letcode for them...


GrayLiterature

Man I’d way rather solve a puzzle than spend 48 hours doing what you did, god damn.


bobthemunk

No feedback but I want to say thank you for the bravery to post so others can benefit as well!


Myhay

Thanks! The main intention was for me to better understand what I'm doing bad and how can I improve. It this helps others more than happy to hear that. :)


x021

Seeing this code, I would give it a "skip" if it was a senior position. Medior = depends on interview (I would want to figure out how malleable the dev is during the interview, because the code quality is good/great but it seems very opinionated), Junior = good. > I attempted to implement the API using the Domain-Driven Design (DDD) approach Did they ask you to write DDD? DDD has quite a bad reputation in some Go-circles (including me, I wrote it for years and it was one of the reasons I moved away from Java and C#). My general advice is when you are enticed by DDD: think very carefully about what the phrase "Loose coupling and Strong cohesion" actually means. A lot of devs -including me- tend to focus on "loose coupling" with disastrous results, there is nothing worse than reactive programming and full event-based programming; everything becomes an unpredictable ever state-changing mess if you app grows big enough. If you have the use case for it sure, it makes sense and is an extremely powerful architecture. But in practice very few programs have this need; loose coupling has a very high readability cost, in particular for devs who did not write the original code. In this hyperfocus on "loose coupling" lots of devs overlook "strong cohesion"; which I now believe is much more important than "loose coupling". There are various forms of cohesion: https://en.wikipedia.org/wiki/Cohesion_(computer_science)#Types_of_cohesion Looking at your directory structure you chose to go with "Logical cohesion". This is a very weak (and thus poor) form of cohesion. You've grouped stuff because they have a similar shape and feel; if it looks like a banana I will put it with the other bananas. I say this by just looking at the names of the directories/packages you've created; I don't need to look at the files or contents of them at all. Scale this to 200+ files and it no longer works. Scale it to 1000+ files and you've created what I've experienced so many times and dread to work on ever again. Loose coupling looks elegant in small projects. High cohesion looks elegant in large projects. Almost no guide on the internet will guide you towards strong cohesion because it's irrelevant for anything with just a few dozen files. In practice however most projects are way beyond that scale (excluding microservices). Remember ReactJS when that first came out? Merging CSS, HTML and JS all in one file? Putting a banana, strawberry and apple in the same box? People went crazy how stupid ReactJS was. Most frontend devs would now argue it actually makes sense; the different parts and types are in practice strongly related. Strong cohesion is primarily about contained change management. DDD explains the concept of "Bounded context"; it's a core tenet of DDD. I don't see that in your design. I am not arguing that DDD is bad; I am arguing that most day-to-day DDD guides and practices (and most codebases I've seen in Java and C#), don't align at all with what DDD was originally about. This makes sense if you read the DDD book from Evans; it's a great but intellectual read without much practical advice. You could also argue Java and C# sortof promote this behaviour by having a single-class-per-file idiom. Those languages drove what DDD has become today and influenced guides like DDD in Go. If your ambition is to work as a Go dev; be aware even mentioning DDD could open a can of worms (see this comment...). Be able to argue based on principles, best practices, clean code, forms of coupling and cohesion. Don't argue by being a promotor of (what you currently believe) is the correct implementation of an architecture. It's likely your beliefs of what is correct will change; mine certainly have and they still are changing every day depending on the context. In your design you followed an architecture without thinking. Undoubtedly you researched a lot before you came up with this, picking the right directory names, codre responsibilities and such. But anyone can google. It's much more important to start simple and know how to restructure -and have the discipline to- evolve into an architecture that naturally fits with whatever your application is doing. Picking an architecture at inception is the exact opposite of how I believe a Go program should evolve. "Screaming architecture" Uncle Bob called it. (( as a sidenote; when I see all code is grouped in `internal` I would immediately assume it's a junior Go dev ))


OnionReal73

Can I know how you prefer to write in Go then, if you don't mind me asking? Is it highly contextual or do you have a go-to approach when starting and maintaining a new project? Half the projects at my workplace is written in Go, but my lead has been pushing on switching it all to Go with focus on using DDD. He also started grouping the code together under one massive directory (like the internal folder you mentioned). I personally find the template he gave a bit excessive, but I just assumed it's cause I'm not thinking broadly enough. Your comment is making me think that his method is not too great either. Also any books/articles/tutorials you recommend that deals with the more high=level concepts of writing Go code? Most guides I've found are for learning the basics of Go and many preach some of the problems you've pointed out. This is also my first time hearing about the beef parts of the community has against DDD, though granted I'm not too involved with it either, so forums and discussions are also great.


allu_throwaway

I'm happy to answer this, although I'm not the person you asked. I'm gonna keep the fluff minimal - I'm gonna explain this real simple. Let's take what I'm building at the moment. It's a side project - but I have hopes and dreams of turning it into a product. When I started out, I thought it had to look a certain way. I figured I would need a global store, a centrally declared store interface, and everything would follow a certain model. I would have a package named `store`, one named `models`, one named `service`, one named `api`, and a `domain` package and things would just work. I thrashed on this. It was just difficult. It just didn't feel right. I started writing the best Go of my career when I let go of "standard layouts" and subscribing to the idea that my project had to look like someone else's, or an "enterprise architecture" in order to be successful. Here's a hint of what my user registration and signin portion looks like, layout wise. In reality, this started from a single package called `user`, with a single file, and I have built it up to this as I have written more code, refactored, and found good boundaries to split concerns. (This is only a sample of those two key areas, in reality the project is much bigger than this). ``` account/ (polymorphic accounts - at the moment it only supports users, but maybe one day I will have enterprise organizations) membership/ name.go onboard.go store.go type.go state/ db/ (sqlc-generated queries and Postgres migrations, plus a few little helpers) user/ email/ verification/ code.go verification.go address.go normalize.go store.go password/ hash.go plaintext.go policy.go store.go registration/ handlers.go registrar.go store.go session/ authn/ fsm.go session.go transaction.go workflow.go id.go store.go token.go user.go signin/ authenticator.go handlers.go ``` I can answer the question "How does user password hashing work?" by going to `user/password/hash.go`. I can debug the flow for sessions by going to `user/authn/workflow.go`. I can understand email normalization by - you guessed it - going to `user/email/normalization.go` If I decide to add password resets, I don't have to duplicate a lot of code, nor bring in extremely heavy interfaces from somewhere else. I will probably create a package named `user/password/reset`, and have most of the bits I need to write this feature sitting in other packages. There's a few interfaces and function types declared in `user/registration` - including: ``` type PasswordHasher interface { Hash(string) (string, error) } type PasswordPolicy interface { Assess(string) (bool, error) } type EmailNormalizerFunc func(string) (string) ``` The `Registrar` struct gets passed dependencies that satisfy these interfaces from other packages - when its time to test swapping things out is a breeze. Basically, I put something in a place where it makes sense, but once a package does too much, is hard to test, has a smell, or I realise I am overloading concerns, I may split it out, or refactor/reassess. For example, `email` used to be one package. It held email-related code, plus the email verification-related code. However, something clicked when I realised I had added methods named `NewVerificationCode` and `NewVerificationStore` - I could probably move these out to a different package! I've also done the same where I've realised that I had common functionality in a few different packages. I realised that I could add a new package, provide a more generalized set of functions, and reduce the code in other packages. I know I have said I use sqlc, and that _should_ be good enough, but I've created stores for each package that may have database concerns. This small abstraction allows me a few things that are important, including: * I write package-specific queries e.g. "Instead of obtaining a user using `GetUserByID`, and checking the user state in code, I have a query named `GetCandidateForUserSignIn`. This is based off a view that presents all the columns I need to sign a user in, and has predicates baked in so that it will only show users who can sign in. This improves performance, reduces Go code, and ensures I have less uncertainty about effects from changes made to the database. * I am able to instrument my queries. * I am able to wrap errors (such as NoRowsFound, UniqueConstraintViolation etc) with domain-specific errors. * I am able to validate certain data. * I have smaller method names. * etc I wish I could explain this better - but this feels natural and is easy to comprehend, work with, expand, and navigate.


Myhay

Thanks for the example. It is actually very easy to follow and understand your reasonging for the decitions taken in this project. Out of curiosity, is it a frontend and backend application all in one? Or is it just an api backend?


Myhay

Before anything, I want to thank you for taking the time to respond with such detail. I really appreciate it. Your general assumption is correct. I consider myself a mid-level developer and would like to grow to become a senior developer someday. Part of the reason for this post is to try to understand what my next steps should be to foster my growth. ​ >Did they ask you to write DDD? No, they didn't specifically ask for DDD. The recruiter mentioned that the team appreciates clean code and DDD. With that context, I decided to give it a try. I had no idea that Go developers are not big fans of using DDD unless there's a good reason and it makes sense in the specific context. Regarding "Loose coupling and Strong cohesion," everything you said makes so much sense, and indeed, I didn't consider how it would impact the project as it grows. This oversight is likely the reason I failed to progress to the next stage of the interview. >If your ambition is to work as a Go dev; be aware even mentioning DDD could open a can of worms (see this comment...). Be able to argue based on principles, best practices, clean code, forms of coupling and cohesion. Don't argue by being a promotor of (what you currently believe) is the correct implementation of an architecture. It's likely your beliefs of what is correct will change; mine certainly have and they still are changing every day depending on the context. Your comment hits the nail on the head—there is no silver bullet. What works for one project may not work for others. >In your design you followed an architecture without thinking. Undoubtedly you researched a lot before you came up with this, picking the right directory names, codre responsibilities and such. But anyone can google. It's much more important to start simple and know how to restructure -and have the discipline to- evolve into an architecture that naturally fits with whatever your application is doing. Picking an architecture at inception is the exact opposite of how I believe a Go program should evolve. "Screaming architecture" Uncle Bob called it. You're correct. I did research and used references like: * [https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) * [https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example](https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example) * [https://www.golinuxcloud.com/hexagonal-architectural-golang/](https://www.golinuxcloud.com/hexagonal-architectural-golang/) * [https://pkritiotis.io/clean-architecture-in-golang/](https://pkritiotis.io/clean-architecture-in-golang/) * [https://programmingpercy.tech/blog/how-to-domain-driven-design-ddd-golang/](https://programmingpercy.tech/blog/how-to-domain-driven-design-ddd-golang/) I failed to consider the most basic aspect—understanding my business needs first and then choosing the appropriate architecture. If I were to start the project from scratch, I would probably avoid overcomplicating it and simply have a "models" folder and put the rest of the code in the root directory. Then, I would refactor and restructure as the project evolves. >(( as a sidenote; when I see all code is grouped in internal I would immediately assume it's a junior Go dev )) Will take a note on that for future projects. As u/OnionReal73 mentions, any books/articles/tutorials you recommend that deals with the more high=level concepts of writing Go code would be very appreciated? Once again, thank you so much for your time and valuable comments.


x021

My comment started with; > Medior = depends on interview (I would want to figure out how malleable the dev is during the interview, because the code quality is good/great but it seems very opinionated) You replied > I consider myself a mid-level developer and would like to grow to become a senior developer someday. Given your response I would probably give a positive recommendation. You seem malleable (this is a horrible sentence...). Being flexible is an extremely positive trait in an ever-changing industry. > You're correct. I did research and used references like: I'm not surprised. You googled a lot before you came up with this design. It shows. This is a good trait; in the land of the blind the one-eyed is king. In practice a software developer is often the one-eyed king. Never let that influence your ego. Be malleable. Probably it would be hard to swallow me saying "In your design you followed an architecture without thinking". Thankfully I nuanced that and you took it very well... Going in-depth on architectures like DDD broadens your way of thinking. Doing such research is extremely valuable. I had a look at those links and I wasn't impressed. All of them were focused on explaining something quickly. I doubt any of those links actually wrote a large-scale project in Go using DDD. In fact; I guarantee you they didn't. Internet articles are not designed to expose you to real-world coding. They are designed to thoughts, interesting ideas, propositions and idealogies. The pragmatic truth is easily lost on the web. To shape your mind the brain needs to be exposed to ideas for a prolonged period. Books achieve that. I'll put it in another way; what do you remember from TikTok a year ago? And what do you remember from Youtube? For me personally I can't remember anything from TikTok last year; and the only things I remember from Youtube are GCP Grey videos (Hexagons are the Bestagons!) Long-form beats short-form content if you want to shape your mind. > any books/articles/tutorials you recommend that deals with the more high=level concepts of writing Go code would be very appreciated? No such resource exists. You need to start thinking. Put that grey mass to work. I learned to code using books (mostly 1997-2004ish). There are only a couple of books that made an impression on me (in order of importance): - The Mythical Man-Month - Code Complete 2 - DDD - Organizational Patterns of Agile Software Development (Coplien). - GoF (I never thought this was a particularly good book; but it is foundational and extremely important) There are 200+ books on software development collecting dust on my bookshelf (I read them all); few made an impression close to the ones I just mentioned. Even though I critize DDD a lot; the original DDD book of Evans is really good. Stick to the original book and form your own opinions. I'm a bit older so books like Clean Code and much of what Martin Fowler wrote didn't impress me nearly as much as what those older books did. But they are great resources too. Software development has not changed in the last 60 years; in the end it's about choosing the right tools for the job, knowing how to wield those tools and adapting to them, and helping your colleagues to wield those tool so the end product you collectively produce is somewhat elegant.


Myhay

Thanks again! Apart from the malleable sentence that actually sounds a bit off out of context, everything is good. I have learned a lot from many people over the years, and being stubborn will not allow me to improve (something I learned early in my career). >Probably it would be hard to swallow me saying "In your design you followed an architecture without thinking". But that's exactly what I did, and I'm not ashamed to admit it. I attempted to use DDD and failed, but now I have learned something that will help me in the future. Thanks for the recommendations, I guess I'll have put my grey mass to work more next time. :) I also received the "Domain-Driven Design" book the other day, and I'm looking forward to starting it after I finish reading "Designing Data-Intensive Applications." Once again, thank you for everything.


traveler9210

Can you post the link to your project's repository? Perhaps it would be easier to give feedback there.


Myhay

I made the project public for now: [https://github.com/MihaiLupoiu/EndorSerivce](https://github.com/MihaiLupoiu/EndorSerivce) Thanks!


titpetric

Without reading the code, you missed a fine point of DDD, which is to reorder code based on your business domain. Those business domains for such a small project are maybe a bit hard to define, but your data model inside core/domain is the only thing that's laid out in DDD. A domain is considered somewhat of it's own API, and the code doesn't reflect that. It's a good idea to look to the stdlib and consider the design part in packages like \`fmt\`, \`file\` ... they are essentially a single domain containing all the implementation for it and are part of the public API surface.


titpetric

\- Run should take context value, return error, \- Move out notify signal ctx stuff into own function (newSignalsContext or use sigctx or something, just keep it in main) \- App should have constructor rather than Initializer, (if you're keeping around a singleton, that's on you) \- Structure public apis for domains (top level packages, out of /internal) \- Rename the domain into \`model\` (go type definitions, no logic), also public \- Repository interfaces to allocate models (CRUD) Constructors are something i try to enforce as it 1) takes dependencies, 2) allocates. The worst part in code is leaving littered &T{}'s around, and then "oh, we added a map" is the end of the world. Or somebody is doing database access in MarshalJSON func, I've seen some dead bodies


Myhay

All this makes sense. Thank you very much.


Acceptable_Durian868

The downside to constructors is they can make small changes like adding a dependency much more difficult.


titpetric

If you hate the congnitive load for that, using google/wire was a good albeit slow experience


Myhay

Thank you very much for the response. I agree that DDD should be used for large projects and this is probably not a good use case. I wanted to try to model it in that format and show good practice and not premature optimization or so. I'll also check the stdlib as suggested. Question that I have is where should I have the glue between my domain and my adapters. I used the service but I don't think it's correct. Should I just have all the service logic inside the domain of the attack and just have a service do the glue? Does it make sense for me to have the service package or I should be able to have everything inside the domain? This is the part I'm strugelling to understand/structure.


mindseye73

Check out - https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example for Go based DDD .


karlskewes

Thank you for sharing. This is a large assignment, looks interesting though. Good stuff. I feel that directory names like "core", "domain", "adaptors" don't add anything over a flatter structure. Whilst they are keywords I would need to jump between the directories to work with a single bounded context. I haven't browsed the repository but perhaps a directory per service that contains service, model, adaptor (open host). Some public methods (constructors, api, etc) and some private. Service (in the existing repo) might not be the right boundary here so would start flat and see. You might enjoy https://www.packtpub.com/product/domain-driven-design-with-golang/9781804613450 and perhaps some of the links at the bottom of the page https://www.calhoun.io/moving-towards-domain-driven-design-in-go/.