Re: "I think the community needs more explicit direction...."
I teach programming at the local university and have found that one of the biggest mental blocks which students need to overcome is the assumption that there is only one way to solve any problem, and that the job of a programmer is to find that way.
This mindset makes it really hard to teach refactoring, particularly in the context of eager new practitioners of TDD who want to rush on to the next test as soon as the first one passes.
I'm not sure where this attitude comes from. Perhaps the way that programming and/or software development has been reframed as "coding", and the implications of the name, or maybe that first experiences in the field often come from poking at HTML and CSS with a stick to get something that looks right, and then copy/pasting a bit of Javascript or PHP to make something happen, or perhaps the prevalence of fill-in-the blanks frameworks, or perhaps that so many students prefer to learn by following a linear set of steps from YouTube, Who knows
I'd love to investigate this in more detail, as I too, can see how much new developers struggle with the concept and skills of refactoring.
If it helps, I have found that I get slightly better traction if I lead with the idea of "code smells."
Frank Carver
|
Re: "Find or Create" functions: a discussion
I like to separate between the ideal and pragmatic designs.
If an idealized model says the record should already exist, but for pragmatic reasons we haven't created it yet, allocating on fetch is perfectly reasonable.? For example, initializing a friends list only when a an actual friend is being added sounds fine to me. Treating the Command part of a Query as a hidden implementation detail is OK.
However, I get more concerned when the value being created is more complicated. Partially initialized objects are scary.? Worse is if there might be business rules that prohibit certain values from being created. Having a get() throw IllegalValue error would be surprising.
I'm fine with the general pattern for trivial values but creating a patient record as a side effect feels a little off. Your one line example crosses my threshold of complexity but? I can change the nouns and be fine with it.
-- Jeff Bellegarde
toggle quoted message
Show quoted text
Pragmatically, in the vast majority?of applications much less data is required for finding an entry than creating it.? Creating an entry with so much missing data can create data integrity problems.
So, given that findOrCreate() should need all the data that Create() would need (not just identifying information), I would find it cleaner to just allow Create to return the object whether it was created or already existed (and signal that the entry already existed if the client code cares).??
On Sun, Nov 24, 2019 at 1:55 AM J. B. Rainsberger < jbrains762@...> wrote: Hi, folks. An old issue came back to the surface this month in consultation with clients and I'd like your opinion. It regards the old "find or create" pattern. It seems to violate Command/Query Separation (as I understand it), but it seems handy and harmless, so I'd like to find out more about what you folks think about it. Benign? Problematic?
I imagine using this with the Repository pattern. Let's say we register a patient in a medical environment and so we need a UI that reduces as much as possible the number of steps. We don't want to force the user to look up a patient just to discover that the hospital has no record of them, so we allow the user to enter some basic identifying information. This information suffices to either find an existing patient or create a new one if our database doesn't know that patient. The result is something like
Patient registeredPatient = patientRepository.findOrCreate(patientIdentifyingInformation);
The identifying information might have basics like name, date of birth, it doesn't matter. We can guarantee that registeredPatient now represents an Entity in our system, either because we found someone that matched the identifying information or because we created one.
This appears to violate CQS, but it seems like a good thing to have. Some individuals struggle with this, because they don't know whether this is an area where CQS "doesn't matter" or an area where CQS is trying to teach them something and they can't see what they're meant to learn. I haven't thought about this in depth in years, so I feel the same way right now. Drawbacks? Alternatives?
I was also thinking about how to design this, and it seems to me like a special case of getOrAbsent(), so that I could implement the generic findOrCreate() algorithm with something like
repository.find(identifyingInformation).orElse(T::createFromIdentifyingInformation)
where find() returns Maybe TIdentifyingInformation and T has a named constructor for creating a T from a TIdentifyingInformation. I'm assuming here that TIdentifyingInformation is enough to provide all the mandatory properties of T.
With this design, I don't need a single findOrCreate() function any more, because the pieces find() and orElse(T::create) communicate the idea well enough.
Thoughts? I'm happy to see the discussion meander. Is this a totally-solved issue and there's one clear good way to proceed? or is it more a matter of context or preference? -- J. B. (Joe) Rainsberger :: :: ::
|
Re: "I think the community needs more explicit direction...."
In my view TDD requires three distinct skills to be "fully effective": 1. Writing the test first (as opposed to after) 2. Writing good tests (as opposed to crappy tests: You can write crappy tests first..) 3. Understanding design (this is where the initial test guides our design for the entry point in the unit of work, and the refactoring is us guiding the code into a more maintainable and readable internal design)
Teaching all three at the same time is hard. I've seen people try to just jump into TDD in places and failing because climbing this wall is too high in one go. I've created courses separately for the design part and named them "refactoring skills for TDD" because even in a 5 days class, teaching the first two skills is difficult enough.
I'm not including here the skill of refactoring?LEGACY code into a testable state. I do teach that stuff, but there seems to be a distinction, at least to me between continuous refactoring during new code vs legacy (not tests) refactoring which is a much more dangerous state and might even require integration tests to begin. Perhaps we are really talking about 4 skills and not three.
toggle quoted message
Show quoted text
On Mon, Nov 25, 2019 at 10:56 AM Brian Marick < marick@...> wrote:
> On Nov 23, 2019, at 12:32 PM, J. B. Rainsberger <jbrains762@...> wrote:
> I have noticed something when programmers try to practise TDD: they don't seem to learn how to refactor. I don't mean to state this so harshly, but it seems that they struggle a lot more with refactoring than I did when I started.
This seems really important. Do other people see the same thing?
|
Re: We are back on the air!
toggle quoted message
Show quoted text
On Thu, Nov 14, 2019 at 6:05 AM Avi Kessner < akessner@...> wrote: Is this the diagram you wanted to share?
I would recommend making a box which is part of the flow rather than some external dotted lines. For example, maybe the box should read, " Apply SOLID principles" as part of the refactoring, and then before the next failing test have something like, "design the contract"
I think the community needs more explicit direction on the design being done in those phases.
On Tue, Nov 12, 2019 at 11:27 AM Roy Osherove < roy@...> wrote: ? Awesome.? I sent a new thread to the group about tdd diagrams.? Was it received?
Maybe.
If you sent it to the Yahoo! group, then no.
Please try again--at least until we figure out the transition. :) I want to see it! -- J. B. (Joe) Rainsberger :: :: ::
|
Re: "Find or Create" functions: a discussion
First thing first, it looks strange to look for something by giving the values it should fetch. On the UI side I would usually have a get that fetches me data to get a state of my application. And at some time, I could decide to send a request to create something but I would do so explicitly. To continue with your question, to me, it looks like the?
findOrCreate? is doing one thing too much. If you are experiencing performance?problems, it could be alright to do so. But in the long run, it would be best to split it into two actions as you stated.? If you have high concurrency you might also think about these two actions in a transaction, but if the race is not a tight one, you might survive not having any transaction at all. my 2 cts, have?a nice evening,
toggle quoted message
Show quoted text
Le?dim. 24 nov. 2019 ¨¤?15:01, Steve Gordon < sgordonphd@...> a ¨¦crit?: Pragmatically, in the vast majority?of applications much less data is required for finding an entry than creating it.? Creating an entry with so much missing data can create data integrity problems.
So, given that findOrCreate() should need all the data that Create() would need (not just identifying information), I would find it cleaner to just allow Create to return the object whether it was created or already existed (and signal that the entry already existed if the client code cares).??
On Sun, Nov 24, 2019 at 1:55 AM J. B. Rainsberger < jbrains762@...> wrote: Hi, folks. An old issue came back to the surface this month in consultation with clients and I'd like your opinion. It regards the old "find or create" pattern. It seems to violate Command/Query Separation (as I understand it), but it seems handy and harmless, so I'd like to find out more about what you folks think about it. Benign? Problematic?
I imagine using this with the Repository pattern. Let's say we register a patient in a medical environment and so we need a UI that reduces as much as possible the number of steps. We don't want to force the user to look up a patient just to discover that the hospital has no record of them, so we allow the user to enter some basic identifying information. This information suffices to either find an existing patient or create a new one if our database doesn't know that patient. The result is something like
Patient registeredPatient = patientRepository.findOrCreate(patientIdentifyingInformation);
The identifying information might have basics like name, date of birth, it doesn't matter. We can guarantee that registeredPatient now represents an Entity in our system, either because we found someone that matched the identifying information or because we created one.
This appears to violate CQS, but it seems like a good thing to have. Some individuals struggle with this, because they don't know whether this is an area where CQS "doesn't matter" or an area where CQS is trying to teach them something and they can't see what they're meant to learn. I haven't thought about this in depth in years, so I feel the same way right now. Drawbacks? Alternatives?
I was also thinking about how to design this, and it seems to me like a special case of getOrAbsent(), so that I could implement the generic findOrCreate() algorithm with something like
repository.find(identifyingInformation).orElse(T::createFromIdentifyingInformation)
where find() returns Maybe TIdentifyingInformation and T has a named constructor for creating a T from a TIdentifyingInformation. I'm assuming here that TIdentifyingInformation is enough to provide all the mandatory properties of T.
With this design, I don't need a single findOrCreate() function any more, because the pieces find() and orElse(T::create) communicate the idea well enough.
Thoughts? I'm happy to see the discussion meander. Is this a totally-solved issue and there's one clear good way to proceed? or is it more a matter of context or preference? -- J. B. (Joe) Rainsberger :: :: ::
|
Re: "Find or Create" functions: a discussion
In the case of identifying for medical reasons, it would be vital to distinguish the person: a "find or create" method would invite duplicate records, and loss (or at least unawareness) of other medical history.
toggle quoted message
Show quoted text
Pragmatically, in the vast majority?of applications much less data is required for finding an entry than creating it.? Creating an entry with so much missing data can create data integrity problems.
So, given that findOrCreate() should need all the data that Create() would need (not just identifying information), I would find it cleaner to just allow Create to return the object whether it was created or already existed (and signal that the entry already
existed if the client code cares).??
On Sun, Nov 24, 2019 at 1:55 AM J. B. Rainsberger < jbrains762@...> wrote:
Hi, folks. An old issue came back to the surface this month in consultation with clients and I'd like your opinion. It regards the old "find or create" pattern. It seems to violate Command/Query Separation (as I understand it), but it seems handy and harmless,
so I'd like to find out more about what you folks think about it. Benign? Problematic?
I imagine using this with the Repository pattern. Let's say we register a patient in a medical environment and so we need a UI that reduces as much as possible the number of steps. We don't want to force the user to look up a patient just to discover that
the hospital has no record of them, so we allow the user to enter some basic identifying information. This information suffices to either find an existing patient or create a new one if our database doesn't know that patient. The result is something like
Patient registeredPatient = patientRepository.findOrCreate(patientIdentifyingInformation);
The identifying information might have basics like name, date of birth, it doesn't matter. We can guarantee that registeredPatient now represents an Entity in our system, either because we found someone that matched the identifying information or because
we created one.
This appears to violate CQS, but it seems like a good thing to have. Some individuals struggle with this, because they don't know whether this is an area where CQS "doesn't matter" or an area where CQS is trying to teach them something and they can't see
what they're meant to learn. I haven't thought about this in depth in years, so I feel the same way right now. Drawbacks? Alternatives?
I was also thinking about how to design this, and it seems to me like a special case of getOrAbsent(), so that I could implement the generic findOrCreate() algorithm with something like
repository.find(identifyingInformation).orElse(T::createFromIdentifyingInformation)
where find() returns Maybe TIdentifyingInformation and T has a named constructor for creating a T from a TIdentifyingInformation. I'm assuming here that TIdentifyingInformation is enough to provide all the mandatory properties of T.
With this design, I don't need a single findOrCreate() function any more, because the pieces find() and orElse(T::create) communicate the idea well enough.
Thoughts? I'm happy to see the discussion meander. Is this a totally-solved issue and there's one clear good way to proceed? or is it more a matter of context or preference?
--
J. B. (Joe) Rainsberger :: ::
::
|
Re: "Find or Create" functions: a discussion
When developing web applications we should bear in mind that some people that do not want to have I.e. any citizenship, this would lead to dirty data
Mit Freundlichem Gr¨¹?en?
Ahmet Murati
Software developer & Translator
E-mail:?ahmet.murati@...
E-mail:?ahmetmurati@...
Handy:?+4915123015776
toggle quoted message
Show quoted text
Hi, folks. An old issue came back to the surface this month in consultation with clients and I'd like your opinion. It regards the old "find or create" pattern. It seems to violate Command/Query Separation (as I understand it), but it seems handy and harmless,
so I'd like to find out more about what you folks think about it. Benign? Problematic?
I imagine using this with the Repository pattern. Let's say we register a patient in a medical environment and so we need a UI that reduces as much as possible the number of steps. We don't want to force the user to look up a patient just to discover that
the hospital has no record of them, so we allow the user to enter some basic identifying information. This information suffices to either find an existing patient or create a new one if our database doesn't know that patient. The result is something like
Patient registeredPatient = patientRepository.findOrCreate(patientIdentifyingInformation);
The identifying information might have basics like name, date of birth, it doesn't matter. We can guarantee that registeredPatient now represents an Entity in our system, either because we found someone that matched the identifying information or because
we created one.
This appears to violate CQS, but it seems like a good thing to have. Some individuals struggle with this, because they don't know whether this is an area where CQS "doesn't matter" or an area where CQS is trying to teach them something and they can't see
what they're meant to learn. I haven't thought about this in depth in years, so I feel the same way right now. Drawbacks? Alternatives?
I was also thinking about how to design this, and it seems to me like a special case of getOrAbsent(), so that I could implement the generic findOrCreate() algorithm with something like
repository.find(identifyingInformation).orElse(T::createFromIdentifyingInformation)
where find() returns Maybe TIdentifyingInformation and T has a named constructor for creating a T from a TIdentifyingInformation. I'm assuming here that TIdentifyingInformation is enough to provide all the mandatory properties of T.
With this design, I don't need a single findOrCreate() function any more, because the pieces find() and orElse(T::create) communicate the idea well enough.
Thoughts? I'm happy to see the discussion meander. Is this a totally-solved issue and there's one clear good way to proceed? or is it more a matter of context or preference?
--
J. B. (Joe) Rainsberger :: ::
::
|
Re: "I think the community needs more explicit direction...."
On Nov 23, 2019, at 12:32 PM, J. B. Rainsberger <jbrains762@...> wrote: I have noticed something when programmers try to practise TDD: they don't seem to learn how to refactor. I don't mean to state this so harshly, but it seems that they struggle a lot more with refactoring than I did when I started. This seems really important. Do other people see the same thing?
|
Re: We are back on the air!
Thank you so much JB! It's appreciated ;-)
|
Re: "I think the community needs more explicit direction...."
Steve (et al) So here is the real question I have I've gotten older, and it seems out of touch with a lot of the younger developers
I was brought in to mentor them on this stuff, refactoring, TDD etc (and of course get out code, setup CD/CI etc etc - of course all at the same time ASAP)
How do you mentor 20 somethings when they won't READ, even short stuff
I tried to get them interested in "Brownfield Development" - Or Feathers, etc - and even when you hand them something as small as a partial chapter, or a simple write-up, you get blown off that they don't read (and this is management, as well as the Jr developers)
I've noticed at the last couple of places. One place - Give them the basic instructions for GIT/TortoiseGIT, no one can be bothered to read it, and one culprit (the CEO no less) would cowboy code, have it on his PC, not in any source control, and yell when you can't get things to work (the only semi working copy of the web site source was on his laptop)
I don't understand how to get through to these folks anymore
Trying to get them to understand WHY you TDD - Why you can't get CI/CD setup on a legacy project in a week (all manual builds off a developers PC, with no tests run, even though there were tests)
toggle quoted message
Show quoted text
On 2019-11-24 19:49, Steven Smith wrote: Autocorrect: yeah->teach
On Nov 24, 2019, at 19:48, Steven Smith via Groups.Io <ssmith.lists@...> wrote: ?I yeah refactoring a lot, and I think practice helps (shocking, I know). I include it in my workshops as well as in free resources on my github (). I also have three different Pluralsight courses on refactoring, including the monster 8 hour course Refactoring Fundamentals. Anyone can access these with a trial account and most enterprise shops I talk to have subscriptions for their folks. But to JB¡¯s point, I do think it¡¯s a part of TDD that newer devs lack confidence and probably skill to perform, at least until they gain experience with it. Steve
|
Re: "I think the community needs more explicit direction...."
Last place I was at ¡°we can¡¯t touch anything in X library, because too much depends on it, and it might break¡±
Of course the way to fix about 50% of their problems was to add methods to the classes there. The whole system structure had a serious case of Class Envy with 4-5 classes in that library, but ¡°too risky¡± Oh well, I¡¯m on the beach, so not my Monkey anymore
-- 73 de KG2V Charlie
toggle quoted message
Show quoted text
On Nov 24, 2019, at 7:49 PM, Steven Smith <ssmith.lists@...> wrote:
?I yeah refactoring a lot, and I think practice helps (shocking, I know). I include it in my workshops as well as in free resources on my github (). I also have three different Pluralsight courses on refactoring, including the monster 8 hour course Refactoring Fundamentals. Anyone can access these with a trial account and most enterprise shops I talk to have subscriptions for their folks.
But to JB¡¯s point, I do think it¡¯s a part of TDD that newer devs lack confidence and probably skill to perform, at least until they gain experience with it.
Steve
On Nov 24, 2019, at 17:14, Hassan Schroeder <hassan.schroeder@...> wrote:
?On Sun, Nov 24, 2019 at 1:40 PM Charles Gallo <Charlie@...> wrote:
Place after place I¡¯ve been in has a small to mid sized collection of tests, that are either not maintained, not run, or not added to, and when you do start adding, management complains. I¡¯ve never been to a place that does more than pay lip service Ouch, that sucks. I'm part of a smallish team, but we deploy via CI/CD and if any of the test suite, code coverage minimum, linter, or typespec checker fails, it doesn't go out.
We reject PRs for not having adequate tests, and recognize people for expanding coverage of existing code.
My condolences on your past employment situations :-)
-- Hassan Schroeder ------------------------ hassan.schroeder@... twitter: @hassan Consulting Availability : Silicon Valley or remote
|
Re: "I think the community needs more explicit direction...."
Autocorrect: yeah->teach
toggle quoted message
Show quoted text
On Nov 24, 2019, at 19:48, Steven Smith via Groups.Io <ssmith.lists@...> wrote:
?I yeah refactoring a lot, and I think practice helps (shocking, I know). I include it in my workshops as well as in free resources on my github (). I also have three different Pluralsight courses on refactoring, including the monster 8 hour course Refactoring Fundamentals. Anyone can access these with a trial account and most enterprise shops I talk to have subscriptions for their folks.
But to JB¡¯s point, I do think it¡¯s a part of TDD that newer devs lack confidence and probably skill to perform, at least until they gain experience with it.
Steve
On Nov 24, 2019, at 17:14, Hassan Schroeder <hassan.schroeder@...> wrote:
?On Sun, Nov 24, 2019 at 1:40 PM Charles Gallo <Charlie@...> wrote:
Place after place I¡¯ve been in has a small to mid sized collection of tests, that are either not maintained, not run, or not added to, and when you do start adding, management complains. I¡¯ve never been to a place that does more than pay lip service Ouch, that sucks. I'm part of a smallish team, but we deploy via CI/CD and if any of the test suite, code coverage minimum, linter, or typespec checker fails, it doesn't go out.
We reject PRs for not having adequate tests, and recognize people for expanding coverage of existing code.
My condolences on your past employment situations :-)
-- Hassan Schroeder ------------------------ hassan.schroeder@... twitter: @hassan Consulting Availability : Silicon Valley or remote
|
Re: "I think the community needs more explicit direction...."
I yeah refactoring a lot, and I think practice helps (shocking, I know). I include it in my workshops as well as in free resources on my github (). I also have three different Pluralsight courses on refactoring, including the monster 8 hour course Refactoring Fundamentals. Anyone can access these with a trial account and most enterprise shops I talk to have subscriptions for their folks.
But to JB¡¯s point, I do think it¡¯s a part of TDD that newer devs lack confidence and probably skill to perform, at least until they gain experience with it.
Steve
toggle quoted message
Show quoted text
On Nov 24, 2019, at 17:14, Hassan Schroeder <hassan.schroeder@...> wrote:
?On Sun, Nov 24, 2019 at 1:40 PM Charles Gallo <Charlie@...> wrote:
Place after place I¡¯ve been in has a small to mid sized collection of tests, that are either not maintained, not run, or not added to, and when you do start adding, management complains. I¡¯ve never been to a place that does more than pay lip service Ouch, that sucks. I'm part of a smallish team, but we deploy via CI/CD and if any of the test suite, code coverage minimum, linter, or typespec checker fails, it doesn't go out.
We reject PRs for not having adequate tests, and recognize people for expanding coverage of existing code.
My condolences on your past employment situations :-)
-- Hassan Schroeder ------------------------ hassan.schroeder@... twitter: @hassan Consulting Availability : Silicon Valley or remote
|
Re: "I think the community needs more explicit direction...."
On Sun, Nov 24, 2019 at 1:40 PM Charles Gallo <Charlie@...> wrote: Place after place I¡¯ve been in has a small to mid sized collection of tests, that are either not maintained, not run, or not added to, and when you do start adding, management complains. I¡¯ve never been to a place that does more than pay lip service Ouch, that sucks. I'm part of a smallish team, but we deploy via CI/CD and if any of the test suite, code coverage minimum, linter, or typespec checker fails, it doesn't go out. We reject PRs for not having adequate tests, and recognize people for expanding coverage of existing code. My condolences on your past employment situations :-) -- Hassan Schroeder ------------------------ hassan.schroeder@... twitter: @hassan Consulting Availability : Silicon Valley or remote
|
Re: "I think the community needs more explicit direction...."
The other problem is there is a megaton of lip service done to TDD. You¡¯ll even have working tests. They make some framework change that breaks the tests, and they stop running them, or ¡°crunch time¡± comes, and management says ¡°I don¡¯t care if it takes longer next quarter, you need to get the following done for the end of the month¡± Place after place I¡¯ve been in has a small to mid sized collection of tests, that are either not maintained, not run, or not added to, and when you do start adding, management complains. I¡¯ve never been to a place that does more than pay lip service
-- 73 de KG2V Charlie
toggle quoted message
Show quoted text
On Nov 24, 2019, at 4:00 PM, Samuel ]slund <samuel@...> wrote:
?Hi all,
Spontaneously I would guess there is a difference in experience between us trying to learn TDD with 5-10 years or more of programming experience and loads of examples of why it was necessary, compared to people learning it today that (I hope) learn it earlier since TDD is not really new any more. I would also guess/hope that TDD now reach a broader range of people than those actively searching for ways to improve.
Regards, //Samuel
|
Re: "I think the community needs more explicit direction...."
Hi all,
Spontaneously I would guess there is a difference in experience between us trying to learn TDD with 5-10 years or more of programming experience and loads of examples of why it was necessary, compared to people learning it today that (I hope) learn it earlier since TDD is not really new any more. I would also guess/hope that TDD now reach a broader range of people than those actively searching for ways to improve.
Regards, //Samuel
toggle quoted message
Show quoted text
On 2019-11-23 19:32, J. B. Rainsberger wrote: I would like to amplify Avi's comment below. I have noticed something when programmers try to practise TDD: they don't seem to learn how to refactor. I don't mean to state this so harshly, but it seems that they struggle a lot more with refactoring than I did when I started. I have this feeling that "we" learned relatively quickly compared to what I see, even comparing myself programming in Java in 1999-2002 to today's programmer also working with Java. And today's Java programmers have better libraries and better tools! They use IDEA and Vavr, but I had Eclipse and plain Java. :) I'd like to know your impressions. Which kinds of problems do people have when they try to learn how to refactor? When they learn how to guide a design to evolve? Why does it seem like they have more difficulty learning this than "we" had? I admit that I probably have some strong cognitive distortion. That's why I'm asking this question. Cheers, -- J. B. (Joe) Rainsberger :: :: :: ---------- Forwarded message --------- From: *Avi Kessner* <akessner@... <mailto:akessner@...>> Date: Thu, Nov 14, 2019 at 5:05 AM Subject: Re: [testdrivendevelopment] We are back on the air! To: <[email protected] <mailto:[email protected]>> Is this the diagram you wanted to share?
I would recommend making a box which is part of the flow rather than some external dotted lines. For example, maybe the box should read, " Apply SOLID principles" as part of the refactoring, and then before the next failing test have something like, "design the contract" I think the community needs more explicit direction on the design being done in those phases. On Wed, Nov 13, 2019, 16:44 J. B. Rainsberger <jbrains762@... <mailto:jbrains762@...>> wrote: On Tue, Nov 12, 2019 at 11:27 AM Roy Osherove <roy@... <mailto:roy@...>> wrote: Awesome.? I sent a new thread to the group about tdd diagrams. Was it received? Maybe. If you sent it to the Yahoo! group, then no. Please try again--at least until we figure out the transition. :) I want to see it! -- J. B. (Joe) Rainsberger :: :: ::
|
Re: "Find or Create" functions: a discussion
Pragmatically, in the vast majority?of applications much less data is required for finding an entry than creating it.? Creating an entry with so much missing data can create data integrity problems.
So, given that findOrCreate() should need all the data that Create() would need (not just identifying information), I would find it cleaner to just allow Create to return the object whether it was created or already existed (and signal that the entry already existed if the client code cares).??
toggle quoted message
Show quoted text
On Sun, Nov 24, 2019 at 1:55 AM J. B. Rainsberger < jbrains762@...> wrote: Hi, folks. An old issue came back to the surface this month in consultation with clients and I'd like your opinion. It regards the old "find or create" pattern. It seems to violate Command/Query Separation (as I understand it), but it seems handy and harmless, so I'd like to find out more about what you folks think about it. Benign? Problematic?
I imagine using this with the Repository pattern. Let's say we register a patient in a medical environment and so we need a UI that reduces as much as possible the number of steps. We don't want to force the user to look up a patient just to discover that the hospital has no record of them, so we allow the user to enter some basic identifying information. This information suffices to either find an existing patient or create a new one if our database doesn't know that patient. The result is something like
Patient registeredPatient = patientRepository.findOrCreate(patientIdentifyingInformation);
The identifying information might have basics like name, date of birth, it doesn't matter. We can guarantee that registeredPatient now represents an Entity in our system, either because we found someone that matched the identifying information or because we created one.
This appears to violate CQS, but it seems like a good thing to have. Some individuals struggle with this, because they don't know whether this is an area where CQS "doesn't matter" or an area where CQS is trying to teach them something and they can't see what they're meant to learn. I haven't thought about this in depth in years, so I feel the same way right now. Drawbacks? Alternatives?
I was also thinking about how to design this, and it seems to me like a special case of getOrAbsent(), so that I could implement the generic findOrCreate() algorithm with something like
repository.find(identifyingInformation).orElse(T::createFromIdentifyingInformation)
where find() returns Maybe TIdentifyingInformation and T has a named constructor for creating a T from a TIdentifyingInformation. I'm assuming here that TIdentifyingInformation is enough to provide all the mandatory properties of T.
With this design, I don't need a single findOrCreate() function any more, because the pieces find() and orElse(T::create) communicate the idea well enough.
Thoughts? I'm happy to see the discussion meander. Is this a totally-solved issue and there's one clear good way to proceed? or is it more a matter of context or preference? -- J. B. (Joe) Rainsberger :: :: ::
|
Re: "Find or Create" functions: a discussion
Apologies for brevity. I¡¯m on my phone.
I think it depends on how transactions are being managed. If called within a transactional scope, I¡¯d be happy with separate ¡°findPatient¡± that returns an optional patient, and a ¡°createPatient¡± that takes the minimal properties needed to record a new patient in the system.? That would allow the properties used to find a patient and the properties used to create a new patient to vary as the application ?requires.
But if transactions & concurrency control are being managed behind the API, they may _have_ to be combined into a single operation.?
toggle quoted message
Show quoted text
On Sun, 24 Nov 2019 at 08:55, J. B. Rainsberger < jbrains762@...> wrote: Hi, folks. An old issue came back to the surface this month in consultation with clients and I'd like your opinion. It regards the old "find or create" pattern. It seems to violate Command/Query Separation (as I understand it), but it seems handy and harmless, so I'd like to find out more about what you folks think about it. Benign? Problematic?
I imagine using this with the Repository pattern. Let's say we register a patient in a medical environment and so we need a UI that reduces as much as possible the number of steps. We don't want to force the user to look up a patient just to discover that the hospital has no record of them, so we allow the user to enter some basic identifying information. This information suffices to either find an existing patient or create a new one if our database doesn't know that patient. The result is something like
Patient registeredPatient = patientRepository.findOrCreate(patientIdentifyingInformation);
The identifying information might have basics like name, date of birth, it doesn't matter. We can guarantee that registeredPatient now represents an Entity in our system, either because we found someone that matched the identifying information or because we created one.
This appears to violate CQS, but it seems like a good thing to have. Some individuals struggle with this, because they don't know whether this is an area where CQS "doesn't matter" or an area where CQS is trying to teach them something and they can't see what they're meant to learn. I haven't thought about this in depth in years, so I feel the same way right now. Drawbacks? Alternatives?
I was also thinking about how to design this, and it seems to me like a special case of getOrAbsent(), so that I could implement the generic findOrCreate() algorithm with something like
repository.find(identifyingInformation).orElse(T::createFromIdentifyingInformation)
where find() returns Maybe TIdentifyingInformation and T has a named constructor for creating a T from a TIdentifyingInformation. I'm assuming here that TIdentifyingInformation is enough to provide all the mandatory properties of T.
With this design, I don't need a single findOrCreate() function any more, because the pieces find() and orElse(T::create) communicate the idea well enough.
Thoughts? I'm happy to see the discussion meander. Is this a totally-solved issue and there's one clear good way to proceed? or is it more a matter of context or preference? -- J. B. (Joe) Rainsberger :: :: ::
|
Re: "I think the community needs more explicit direction...."
? Good, realistic examples are likely more helpful that explicit?instructions.? Different tactics, strategies and principles are more useful in different?contexts for teams with different skills, experience and levels of sophistication.
Yes. I don't know how to get my hands on good, realistic examples, because they are typically confidential material. I don't have the energy to spend months finding and refining good, realistic examples by scouring open source projects. What do other people do?
I encourage students to use their projects as good, realistic examples, but then participate in groups like this one where they can ask questions when they encounter situations that they don't feel comfortable resolving on their own.
-- J. B. (Joe) Rainsberger :: :: ::
|
"Find or Create" functions: a discussion
Hi, folks. An old issue came back to the surface this month in consultation with clients and I'd like your opinion. It regards the old "find or create" pattern. It seems to violate Command/Query Separation (as I understand it), but it seems handy and harmless, so I'd like to find out more about what you folks think about it. Benign? Problematic?
I imagine using this with the Repository pattern. Let's say we register a patient in a medical environment and so we need a UI that reduces as much as possible the number of steps. We don't want to force the user to look up a patient just to discover that the hospital has no record of them, so we allow the user to enter some basic identifying information. This information suffices to either find an existing patient or create a new one if our database doesn't know that patient. The result is something like
Patient registeredPatient = patientRepository.findOrCreate(patientIdentifyingInformation);
The identifying information might have basics like name, date of birth, it doesn't matter. We can guarantee that registeredPatient now represents an Entity in our system, either because we found someone that matched the identifying information or because we created one.
This appears to violate CQS, but it seems like a good thing to have. Some individuals struggle with this, because they don't know whether this is an area where CQS "doesn't matter" or an area where CQS is trying to teach them something and they can't see what they're meant to learn. I haven't thought about this in depth in years, so I feel the same way right now. Drawbacks? Alternatives?
I was also thinking about how to design this, and it seems to me like a special case of getOrAbsent(), so that I could implement the generic findOrCreate() algorithm with something like
repository.find(identifyingInformation).orElse(T::createFromIdentifyingInformation)
where find() returns Maybe TIdentifyingInformation and T has a named constructor for creating a T from a TIdentifyingInformation. I'm assuming here that TIdentifyingInformation is enough to provide all the mandatory properties of T.
With this design, I don't need a single findOrCreate() function any more, because the pieces find() and orElse(T::create) communicate the idea well enough.
Thoughts? I'm happy to see the discussion meander. Is this a totally-solved issue and there's one clear good way to proceed? or is it more a matter of context or preference? -- J. B. (Joe) Rainsberger :: :: ::
|