In order to learn from others I must trust them and open my mind enough to see them as potential teachers even if  they are - apparently - less experienced than me in certain knowledge area. The lessons to be learned may not necessarily come from the things others say or do but from the synergy that comes out a trustworthy relationship. It could be something I say myself as an answer to an unexpected question, one that makes me reflect from a different angle. If I believe I can't learn from other person then I am actually building a mental barrier that will prevent me from learning anything at all. No matter if I think the other person's idea is wrong, my attitude should be open enough to let him do and show me how he works in a way that he feels encouraged to do so, in a safe and collaborative atmosphere. In the case am completely sure that the idea (approach or technique) is not appropriate because I've tried it before and failed (even many times), I may suggest not to go there but still give the others the chance to fail themselves: "Do you really think that is the best way to go?, I don't think it is but if you definitely want to try let's do it". Using questions rather than imperative sentences may instill the right amount of uncertainty in others so that they may also be open minded and discuss better ways.

To build a trustworthy relationship one must learn to listen to others. Be willing to listen to others is the first step. Everyone deserves attention, everyone has stories that are worth listening, and so I do. By letting others express themselves I am giving myself the same margin of trust and tolerance that will make me feel comfortable, creative and valuable when talking to others.

Valuable information is not only in what people say but more importantly, in how they say it. When someone criticizes a third person who is not present during our conversation, that's an opportunity for me to know my speaker better. That third person is pretty much irrelevant in the conversation, the important information comes in the way the speaker expresses her feelings, which let me know about her current mental state and the trigger points that make her upset, annoyed or whatever the feelings are. It's a chance to understand my speaker better and be empathetic. Also the fact that she is verbalizing her thoughts may help her listen to herself and realize that she went too far, this is, verbalizing thoughts may break the negative loop. At least it happens to me, when words are already off my mouth there is no way I can hide, the commitment is done and I end up on a different path often feeling sorry about it.

Some people need silent moments, even "uncomfortably" long ones, in order to start talking. I have to discover what are the conditions under which he will feel safe enough to bring what he knows or whatever he's to say.

Listening to people with full attention requires quite a lot of energy. It's important that I tell them when I am running out of energy so that I can make a break. A few minutes of break per hour make a big difference. Recognizing the right time to leave the conversation for tomorrow is also crucial.

XP Team Building

Let the team walk its path

Every team walks at its own pace and I haven't found a way to speed it up myself, I don't think there is one regardless of whether you are an external coach or a regular member of the team. Trying to push the team will screw up the work environment, the pressure may introduce interpersonal conflicts and severe damages in the quality of the product. I mean, people will do stuff they do know it's wrong because of the sense of emergency. On the other hand trying to pull the team away will give its members the impression that they are not trusted professionals, again causing problems among people and discouraging them from being proactive and willing to learn.
The only way we can help the team progress is to help them walk their own path, removing impediments along the way, answering questions when asked to do so.

I've learned that it's not possible to develop a software product at the speed that I'd like whilst mentoring people. I have to choose either to be a trainer or to be a developer but not both things at the same time because the goals are different. When working as a trainer the goal is that people learn effectively and the best way, for us humans, to learn is by making mistakes that let us learn from ourselves. As a mentor I must combine small theoretical explanations, recommendations and practical examples with time for people to practice and to make their own decisions. They must have the chance to fail and discover. If they don't recognize the failure my mission is to provide feedback and explain why is this or that a mistake and what are the consequences. Deep lessons are learned with pain - success is fantastic but it doesn't teach. However this style of mentoring takes a lot of time, it's probably the top most time consuming way. It's a company wide commitment, an effort for everyone.

Often companies ask us to join them for a new project that must deliver value within a tight schedule and at the same time they want to build up a team of XP developers with people that have never been exposed to XP. I must say this is completely impossible in my experience. If the quality of the product needs to be excellent and deadlines are tight, the best thing the company can do is to hire a seasoned high-performing XP team whose members have been working together for years. On the other hand, if the actual goal is to build up a team, this is an investment that lasts several years. According to my experience, a group of 6 to 8 developers become an XP team after 2 years of training, guidance, reviews and practice. It requires several training courses during the various stages of the transition, practical examples, guidance and moreover it requires small failures (hopefully reversible ones). People have to have the margin to fail and learn from their mistakes. During this period some people will very likely leave the company and some new people will join.

I know the word quality is too abstract, we don't have a definition for what quality is but I am actually talking about well-known maintenance issues. A common mistake beginners make is to write brittle and hard to maintain tests that with time impede developers changing or adding features. Those tests are eventually deleted. It's waste unless you consider this to be another stage in the team's learning curve.
Nobody writes perfect code anyway, every team makes mistakes so the point is not to aim for perfection. The point is to avoid or minimize the amount of things we do know are waste, basically because we have failed in the past.
Hiring seasoned external consultants means they won't make the same mistakes they did in the past although they'll make new mistakes. Every team is different as well as every project.
As a consultant I won't prevent the team from making mistakes when the goal is to train them, instead I'll try to keep the cycles small enough so that mistakes can be fixed soon and cheap. But it's up to the team to ask for feedback and review, my job is not to control the source code - it's to support the team!

In some situations it could be beneficial that a small group of experts write the "walking skeleton" with the first group of product features in order to define architecture, style and conventions that others can follow. It serves to explore risks and exposes an example that others can follow. There are several perils of this approach though. I'd be careful with it - sorry don't feel like writing about these drawbacks now.

EventBus in a Windows 8 app

eventBusWinAppHow to communicate different pages of a Windows 8 App? How to manage the life cycle of the pages?

Pages are instantiated by the framework when asked to navigate:

*.xaml.cs:

  1. frame.Navigate(typeof(MyPage));

It will be a new instance of MyPage everytime unless the pages are cached. To set up the page cache, add this line to the beginning of

MyPage.xaml:

  1. <Page
  2. x:Name="pageRoot"
  3. x:Class="MyNamespace.MyPage"
  4. NavigationCacheMode="Enabled"
  5. ...
  6.  

 
Cache can be clear as described here as long as the NavigationCacheMode is "Enabled" and not "Required". The "Required" pages can't be deleted from the cache as explained by Andreas Hammar.

The page's life cycle is controlled by the framework although we can configure the cache, therefore my objects - those whose life cycle is controlled by myself - should not reference pages in order to avoid memory leaks and undesired side effects.

We've decided that Pages and other UserControls are the ones which create and manage ViewModel's life cycle (we're using MVVM pattern). The page's codebehind (MyPage.xaml.cs) contains a reference to ViewModel.cs which is a plain old C# object that contains the GUI logic. MyPage instantiates the ViewModel in its constructor. So there should be no references to ViewModels from other objects.

MyPage.xaml.cs

  1. public MyPage(){
  2. InitializeCompontent();
  3. ViewModel = new ViewModel();
  4. DataContext = ViewModel;
  5. }

However I can't avoid references to the ViewModel from the EventBus (simple pubsub static class) because it's the mechanism we are using to communicate different parts of the application:

ViewModel.cs:

  1. public void SubscribeToEventBus(){
  2. EventBus.Subscribe(this); // static method
  3. }
  4. public void UnsubscribeEventBus(){
  5. EventBus.Unsubscribe(this); // static method
  6. }

To work around this problem, I make sure the ViewModel contains references to the bus only when it's active (visible on screen):

MyPage.xaml.cs:

  1. public override void OnNavigatedTo(){
  2. ViewModel.SubscribeToEventBus();
  3. ViewModel.Initialize(); // optional
  4. }
  5. public override void OnNavigatedFrom(){
  6. ViewModel.UnsubscribeEventBus();
  7. }

When the ViewModel requires some data previously generated in another view and stored in the AppState, it raises an event through the bus saying it's loaded and then the AppState listener replies back through the bus sending the required data.

We haven't found a built-in way to reset the application clearing all data so we've implemented a reset method. The reset is an event triggered by a button in the bottom bar, sent through the bus. Our App.xaml.cs object which is the singleton that starts the application handles the event clearing all data. The sequence is important to avoid memory leaks

App.xaml.cs:

  1. public void Handle(AppReset appResetArgs){
  2. EventBus.Clear();
  3. AppState.Reset();
  4. InitializeServices();
  5. EventBus.Subscribe(this);
  6. ResetXamlCache(); // this must be the last thing to clear
  7. NavigateToRootPage();
  8. }

Only the App object subscribes to the AppReset event. Other global events like a connection failure are also handled by the App object.

A final reflection is,... Why do we need an EventBus at all? We could just inject the same AppState instance in every ViewModel to share data among the different screens. Something like a NavigationService could have solved this for us. The following pseudocode illustrates the idea:

NavigationService.cs:

  1. public static void NavigateTo<T>(){
  2. var frame = (Frame) Window.Current.Content;
  3. var viewModel = Factory.ViewModelFor<T>();
  4. frame.Navigate(typeof (T), viewModel);
  5. }

MyPage.xaml.cs

  1. protected override void OnNavigatedTo(NavigationEventArgs e){
  2. ViewModel = e.Parameter as ViewModel;
  3. }

Then in the Factory we could inject the same AppState to all ViewModels and manage their life cycle. This indirection level could have changed out architecture. A custom base paged could implement the "OnNavigatedTo" to avoid duplication.

Thanks to my friend Juan M. Gomez for the idea of the navigation service.

During Socrates Conference 2015 we decided that it's the right time to jump in ES6 to developer a green field project that our customer is starting. Given that ES6 is already the stable and latest version of JavaScript, it does not make sense to start a new project with ES5, an already old version of the language. With the kind of functional style that we use when coding in JavaScript, the changes are not too big anyway as we are not going to use classes anyway. But we can leverage from block scope with "let" and "const" avoiding the use of "var" from now on. Configuring the tools has taken more time than I thought as the tooling and the ecosystem is changing really fast. You read a recipe from a blog post which is 6 months old and it turns out that most of the stuff described is no longer working. Some npm packages don't work anymore or the behavior at some point is very different from a previous version... apparently things are even more obscure on Windows which is the platform we have to use for this project.

As an example, I installed karma via npm the latest version. But when running gulp from the command line it stopped working with no error message at all, just a line break and back to the prompt. I commented all the lines in the gulpfile.js and then uncommented lines one by one executing gulp each time to discover that "require('karma')" was the reason. So I got into the node repl and type this myself:

var k = require('karma')

The result was the same, node repl exited silently getting me back to the command line prompt. I couldn't find a single way to catch the error although I tried try-catch, signal capturing, domains... and none of that worked. Then I started downgrading the version of the karma package until it worked for me. Version 0.13.3 works but 0.13.4 doesn't. It must be a very specific problem on my machine but I couldn't find any other solution. Eventually we are not using karma for now, we are using jasmine stand alone version and mocha.

This is the simplest gulpfile I was able to get working:

  1. var gulp = require('gulp');
  2. var sourcemaps = require("gulp-sourcemaps");
  3. var mainBowerFiles = require('main-bower-files');
  4. var browserify = require('browserify');
  5. var vinylSource = require('vinyl-source-stream');
  6. var glob = require('glob');
  7. var vinylBuffer = require('vinyl-buffer');
  8. var babel = require('babelify');
  9.  
  10. var globalNamespace = 'mynamespace';
  11. var launcher = './Scripts/src/main.js';
  12. var sourceFiles = 'Scripts/src/*.js';
  13. var specFiles = 'Scripts/spec/*.js';
  14. var libFiles = 'Scripts/dist/vendor/*.js';
  15. var distFolder = "Scripts/dist";
  16. var allFiles = [libFiles, sourceFiles, specFiles];
  17.  
  18. gulp.task('package-src', function () {
  19. var filenames = glob.sync(sourceFiles); // generate array of filenames
  20. return browserify({
  21. entries: filenames,
  22. debug: true
  23. })
  24. .require(launcher, {expose: globalNamespace}) // publish
  25. .on('error', function (err) {
  26. console.error(err); this.emit('end');
  27. })
  28. .transform(babelify)
  29. .bundle()
  30. .pipe(vinylSource('all.js')) // generated output file
  31. .pipe(vinylBuffer()) // required for sourcemaps
  32. .pipe(sourcemaps.init())
  33. .pipe(sourcemaps.write("."))
  34. .pipe(gulp.dest(distFolder));
  35. });
  36.  
  37. gulp.task('package-tests', function (done) {
  38. var filenames = glob.sync(specFiles);
  39. return browserify({
  40. entries: filenames,
  41. debug: true
  42. })
  43. .on('error', function (err) {
  44. console.error(err);
  45. this.emit('end');
  46. })
  47. .transform(babelify)
  48. .bundle()
  49. .pipe(vinylSource('specs.js'))
  50. .pipe(vinylBuffer())
  51. .pipe(sourcemaps.init())
  52. .pipe(sourcemaps.write("."))
  53. .pipe(gulp.dest(distFolder));
  54. });
  55.  
  56. gulp.task('package-vendor', function () {
  57. return gulp.src(mainBowerFiles({filter: '**/*.js'}))
  58. .pipe(gulp.dest(distFolder + '/vendor/'));
  59. });
  60.  
  61. gulp.task('default', [
  62. 'package-src',
  63. 'package-vendor',
  64. 'package-tests'
  65. ]);

The generated package is "all.js" which I include in the html page. The application's entry point is on main.js with exposes a function called startApp.

App starts up at the bottom of the html page:

  1. <script>
  2. var mynamespace = require('mynamespace'); // require function is provided by browserify, no need to include require.js
  3. mynamespace.startApp();
  4. </script>

main.js:

  1. import viewModel from "./viewModel";
  2. import restClient from "./restClient";
  3.  
  4. export function startApp() {
  5. let rc = restClient();
  6. let vm = viewModel(rc);
  7. ko.applyBindings(vm);
  8. };
  9.  

In order to run the tests the most simple choice was Jasmine stand alone, including the generated "specs.js" file in the SpecRunner.html page. As the tests include the production code, the generated file "specs.js" already include all the production code.

tests:

  1. import restClient from "../src/restClient";
  2. import viewModel from "../src/viewModel";
  3.  
  4. describe("make an vehicle order", ()=> {
  5. ...
  6.  

The next step was to include "watchify" in order to rebundle everytime a file is saved.

gulpfile.js:

  1. var gulp = require('gulp');
  2. var sourcemaps = require("gulp-sourcemaps");
  3. var mainBowerFiles = require('main-bower-files');
  4. var browserify = require('browserify');
  5. var vinylSource = require('vinyl-source-stream');
  6. var glob = require('glob');
  7. var vinylBuffer = require('vinyl-buffer');
  8. var watchify = require('watchify');
  9. var babelify = require('babelify');
  10.  
  11. var launcher = './Scripts/src/main.js';
  12. var globalNamespace = 'mynamespace';
  13. var sourceFiles = 'Scripts/src/**/*.js';
  14. var sourceBundle = 'all.js';
  15. var specFiles = 'Scripts/spec/**/*.js';
  16. var specBundle = 'specs.js';
  17. var libFiles = 'Scripts/dist/vendor/*.js';
  18. var distFolder = 'Scripts/dist';
  19. var allFiles = [libFiles, sourceFiles, specFiles];
  20.  
  21. gulp.task('package-src-dev', function() {
  22. bundleWatchify(sourceFiles, sourceBundle);
  23. });
  24.  
  25. gulp.task('package-src', function() {
  26. bundle(sourceFiles, sourceBundle);
  27. });
  28.  
  29. gulp.task('test-dev', function () {
  30. bundleWatchify(specFiles, specBundle);
  31. });
  32.  
  33. gulp.task('package-vendor', function () {
  34. return gulp.src(mainBowerFiles({filter: '**/*.js'}))
  35. .pipe(gulp.dest(distFolder + '/vendor/'));
  36. });
  37.  
  38. gulp.task('default', ['package-src', 'package-vendor', 'test-dev']);
  39. gulp.task('package-dist', ['package-src', 'package-vendor']);
  40.  
  41. function bundleWatchify(sources, output) {
  42. var watchified = watchify(doBrowserify(sources))
  43. .on('update', function (filenames) {
  44. console.log('rebuilding -> ', filenames[0]);
  45. rebundle(watchified, output);
  46. });
  47. return rebundle(watchified, output);
  48. }
  49.  
  50.  
  51. function bundle(sources, output) {
  52. return rebundle(doBrowserify(sources), output);
  53. }
  54.  
  55. function doBrowserify(sources) {
  56. var filenames = glob.sync(sources);
  57. var browserified = browserify({
  58. entries: filenames,
  59. debug: true
  60. });
  61. return browserified;
  62. }
  63.  
  64. function rebundle(b, output) {
  65. return b
  66. .require(launcher, { expose: globalNamespace })
  67. .on('error', function (err) {
  68. console.error(err);
  69. this.emit('end');
  70. })
  71. .transform(babelify)
  72. .bundle()
  73. .pipe(vinylSource(output))
  74. .pipe(vinylBuffer()) // required for sourcemaps
  75. .pipe(sourcemaps.init())
  76. .pipe(sourcemaps.write("."))
  77. .pipe(gulp.dest(distFolder));
  78. }
  79.  

This post has been written on September 14th 2015, if you try to use any of the snippets posted a few months later they probably won't work for you. Versions used:

package.json:

  1. {
  2. "name": "VehicleOrders",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "gulpfile.js",
  6. "devDependencies": {
  7. "babel": "5.8.23",
  8. "babelify": "6.3.0",
  9. "browserify": "11.0.1",
  10. "glob": "5.0.14",
  11. "gulp": "3.9.0",
  12. "gulp-babel": "5.2.1",
  13. "gulp-concat": "2.4.1",
  14. "gulp-sourcemaps": "1.5.2",
  15. "main-bower-files": "2.8.0",
  16. "vinyl-buffer": "1.0.0",
  17. "vinyl-source-stream": "1.1.0",
  18. "vinyl-transform": "1.0.0",
  19. "watchify": "3.4.0"
  20. },
  21. "scripts": {
  22. "test": "echo \"Error: no test specified\" && exit 1"
  23. },
  24. "author": "AIDA",
  25. "license": "ISC"
  26. }

Notes from Socrates 2015

The 5th Socrates Conference in Germany has been my first continental Socrates Conference (I've been to UK twice plus another one in the Canaries). What a mind blowing unconference! I got even more value from Socrates than I expected! this has been the biggest in size ever with almost 190 people. There were so many people that it felt like there were 3 conferences going on at the same time. Even in my last couple of hours in the venue I still found new faces, people I thought I didn't see before. Despite of so many people the atmosphere was excellent as usual and the organization brilliant. Congratulations to the organisers! everything was organised with German precision! Danke schön 😉
Unfortunately Andreas - one of the founders - couldn't join us but I am glad he is recovering well.

I've learned many things, met old friends (although I wanted to spend more time with them!) and made new ones.

I have to thank my friend Ivan (@istepaniuk) for being our host and guide in Amsterdam and for driving us to Soltau. Thank you Imo (@kabomi), Juan (@_jmgomez_) and Adrian (@eidrien) for your fantastic company.

Thank you Pierluigi Pugliese (@p_pugliese) for your excellent job as facilitator and the conversation/coaching we had during lunch time.
Notes:

  • Twitter hashtag was #socrates15
  • Really like the idea of the world cafe on Thursday to get people thinking on what they would like to happen the next days.
  • There are so many proposals that is hard to remember some sessions when looking at the panel, that makes it even harder to choose sessions. Next time I'll write down in paper exactly the minimum and most effective sentences I'll need to present my proposal so that I'll just have to read when the time arrives. Talking to the mic is a bit unconfortable when not used to it.
  • I'll book more time to spend with my friends and specially with my colleagues.
  • In the sessions intended for me to ask questions, to be taught something or to have a discussion is not necessary to have slides but it's worth preparing the session. It could be a script, a few examples to discuss on, a set of questions... something to facilitate the session and an idea of what to achieve with it.
  • Some of the most valuable moments happen out of the scheduled time table or in those with a small group of people. Next time I'll plan to spend about half of each day out of the rooms, talking and coding with people or even coding on my own in places where others can see me and join.
  • I will propose maximum one session per day, then I'll feel more relaxed and free to change plans.
  • Sleeping enough is key to enjoy the event until the end, otherwise I am already a zombie at the end of the first day. Nightly discussions are super interesting, I'll probably oversleep in the morning at least one day to be fresh and ready for the night.
  • Socrates is an open space where I can go without any session prepared, people are so amazing that there will be for sure a lot of stuff to learn.
  • The idea of workshops on Sundays is brilliant we'll do the same in Socrates Canaries. I've learned many things on Sunday working on a walking skeleton and solved many doubts regarding DDD. Special thanks to Bardford (@gruebelinchen), Björn, Felipe (@felipefzdz), Martin (@md42), Dimitry (@dimitrypolivaev), Ivan and all the people that stepped in and out during the workshop.
  • It's the right time for our team to embrace ES6 and use it in the project we are starting from scratch. Thank you Wolfram (@wolframkriesing), Marco (@marcoeimrich) and Christian (fonzygruen) for all the insight and information. Thank you also for ES6Katas and Ramda.js - we're gonna use both. Thank you also for valuable resources like NodeSchool and the refactoring kata (refactoring to functional style using Rambda) proposed by Marco.
  • Property-based testing is definitely something I want to try. Thank you for the insight and opinions on when to use it and when not to use it, specially to Pawel  (@pawelduda) and Kuba.
  • The theory of type systems is super interesting, I would like to get the slides from Ivan (@ikusalic) and compare how Type Classes in Scala are different to generics in C#.
  • Informal discussions like "Developer Ethics" are perfect to be hold during lunch time. I'll propose topics for lunch time like in this conference, so that I'll know what to talk about with people I don't know but who share a common interest. Unfortunately tables were not big enough this time to join some lunch discussions.
  • I really like the metaphor of the power lifting in pairs to explain pair programming, thank you Houssam (@houssamfakih) for sharing your experiences along these years of practise.
  • I want to learn more about Event Sourcing and CQRS. This is the second conference where people recommend Event Storming and I haven't used it in a real project. It's on my to-do list.
  • Thank you Tobi (@tpflug) and Chris (@c089) for organising the Power Point Karaoke once again and the variants. Thank you Adrian, Pawel, Christian (@dertseha) & company for such hilarious nights. I plan to play the PP Karaoke on the New Year's eve with family and friends.
  • Choose the blue route in the GPS, not the red one - although Germany is beautiful.
  • 30 mins Gym + 30 mins sauna after the sessions is a must. German get naked in the sauna no matter if there are men and women mixed.

Pdf made up of pictures

I had to embed 50 pictures in a pdf file for print - don't ask me why. Dependencies (on Debian):

  1. apt-get install imagemagick
  2. apt-get install pdftk

This is the Python script. It uses commands "montage" (from imagemagick) and pdftk:

  1. from subprocess import call
  2. import os
  3.  
  4. folder = 'pictures'
  5. images_per_page=4
  6. columns=2
  7.  
  8. print "Work in progress..."
  9.  
  10. files = os.listdir(folder)
  11. files.sort()
  12. pages = [files[i:i+images_per_page] for i in range(0, len(files), images_per_page)]
  13. pages_names = []
  14. for i in range(0, len(pages)):
  15. montage = ['montage']
  16. print "generating page " + str(i)
  17. files_in_page = [folder + os.sep + j for j in pages[i]]
  18. montage.extend(files_in_page)
  19. montage.extend(['-mode', 'Concatenate', '-tile', str(columns) + 'x'])
  20. output_page = folder + os.sep + 'page' + str(i) + '.pdf'
  21. pages_names.append(output_page)
  22. montage.append(output_page)
  23. call(montage)
  24.  
  25. pdftk = ['pdftk']
  26. pdftk.extend(pages_names)
  27. pdftk.extend(['cat', 'output', 'output.pdf'])
  28. call(pdftk)
  29.  

Using C# Collections

There are many ways to work with collections. We are following Microsoft Guidelines for Collections  plus some ideas that Kent Beck explains in Implementation Patterns. I've created a repository with code examples so that anyone can play with them. All the unit tests in the project are green except for two, which are red on purpose to express my surprise when I wrote them, because it's a behavior I didn't expect.

IEnumerable<T> is not dangerous itself, the surprise may come up when the variable is created via a LINQ expression (.Select, .Where ...). Then it may be the case that the query execution is deferred and the behavior of the code is totally unexpected. For this reason we try to avoid IEnumerable<T> and IQueryable<T> as the return type of functions.

Try not to expose collections as much as you can by wrapping them within objects you own which expose only the minimum domain required features. But if you have to expose the collection anyway, in our teams the convention is:

  • ReadOnlyCollection<T> is the type returned to avoid modifications to the number of elements in the collection. (Internally it's just a wrapper around IList).
  • IList<T> is returned when the collection may be modified and the order of the items in the collection is relevant.
  • ICollection<T> is returned when the collection may be modified and we don't care about the order.

Good code expresses the programmer's intent, this is why we choose from the three types above to return collections when we have to.

When it comes to parameters, it's OK to use IEnumerable<T> as a parameter because it's the most generic collection type. Although the caller could send an unresolved Linq query as an argument hidden in the form of a IEnumerable<T>, the method does not have to be defensive about it (in most cases).

Other built-in collections like List<T> are intended to be used internally, only for implementation. So it's OK for a private method to return List<T>. The type Collection<T> actually implements IList<T> so I can't find a good reason right now to use Collection<T> instead of just List<T>.

Identifying aggregates

An aggregate is particular kind of entity so first of all we need to know whether the model is an entity or a value object. Once we know it's an entity let's figure out whether it is an aggregate. A transaction should modify a single aggregate instance so the repositories should work with aggregates when possible - retrieve and save aggregates. The aggregate is an entity that may contain other entities and value objects. Some heuristics to discover which objects should be clustered into aggregates:

------ Aggregate A:
------   -  Entity B
------   -  Value D
------   -  Value E
------   -  Value F

  1. Model true invariants - an invariant is a business rule that must be consistent (transactionally or eventually) : If there is a rule saying that Value F = Value D + Value E, we must ensure that D, E and F are persisted together to always preserve that rule.
    "Aggregate is one that can be modified in any way required by the business with its invariants completely consistent within a single transaction".
  2. If we need transactional consistency in A, then although B is an entity, it doesn't make much sense to use B out of A's context, I mean, there is no reason for an entity of kind B to be modified or loaded without aggregate A - Imagine that A is an Invoice and B is an Invoice Line.
  3. Try to design small aggregates despite of the desire for compositional convenience.
  4. "Just because you are given a use case that calls for maintaining consistency in a single transaction doesn't mean you should do that. Often, in such cases, the business goal can be achieved with eventual consistency between Aggregates." 

The truth is that is really hard to model things up-front. Our current approach is to model the minimum we need for an action to be performed and then evolve. However the fact that we tried to identify aggregates in front of the whiteboard led us to a nice Context Map drawing session that provided us with a better understanding of the domain.

Source: Implementing Domain Driven Design - Vaughan Vernon

Cualquiera que esté aprendiendo puede beneficiarse de tener un blog. En la profesión de desarrollador uno aprende constantemente por lo que tener un blog es imprescindible en mi opinión. Es importante que entiendas que estas escribiendo para tí, para tu "yo" del futuro. No escribes un blog para los demás, como lo hacen los bloggers que ya tienen mucha experiencia y ganan dinero escribiendo, al estilo de los periodistas o escritores que tienen columnas en períodicos. Esto de ganar dinero escribiendo no tiene nada que ver con un blog personal. Los objetivos son diferentes. Por tanto las entradas de tu blog no tienen que ser perfectas, son meros apuntes, igual que los que tomabas cuando ibas a clase. A veces tienes más tiempo y entonces son resúmenes más que apuntes, notas sintetizadas. Tienes que ocuparte de escribir solo la información que necesitas para que tú misma/mismo seas capaz de entender lo que querías decir en aquel post de hace 6 meses. Nada más. Asi dejará de preocuparte que los demás critiquen tu blog y tardarás menos tiempo en añadir nuevas entradas.
Digan lo que te digan, tu blog es tuyo y puedes hacer con él lo que quieras. Hace tiempo despubliqué un post de crítica injustificada a Git porque me dí cuenta que no tenía sentido y que causaba confusión, fue un error haber publicado esos pensamientos porque ni me ayudaban a mi ni a nadie. Alguna gente me dijo que no podía despublicar, que eso rompía links y que no estaba bien.... pero es mi blog y hago lo que creo oportuno con él.

Lo ideal es que escribir un post te lleve máximo una hora y si lo puedas hacer en 15 minutos, mejor que mejor. Yo de media tardo unos 30 minutos. Si te va a llevar más de eso entonces te dará pereza escribir y terminarás por abandonar el blog. Es decir, la barrera de entrada que te pones para escribir, debe ser lo mas baja posible.
A alguna gente le funciona convertir notas del Evernote en posts de blog directamente.

Cuando aprendes algo, escribir sobre ello es una de las mejores formas de comprender mejor lo aprendido. Es un gran refuerzo de tu aprendizaje. Además es un tangible, una forma fácil de poner en valor la inversión que haces estudiando. Por ejemplo si lees un libro técnico y escribes un post comentando cada capítulo, asimilarás mejor el libro y tendrás un resumen estupendo al que acudir cuando haya que recordar.
Escribir te obliga a buscar un poquito más de información para despejar pequeñas dudas que no te habias planteado hasta que tienes que explicar lo aprendido.

Como son tus propios apuntes, si en el futuro necesitas recordar cómo se hacía algo que ya resolviste y que documentaste, te será más rápido recordarlo leyendo tus propios apuntes que volviendo a leerte varios hilos de StackOverflow. Yo me he arrepentido varias veces de no bloggear ciertas cosas.

Una vez que escribes sobre una idea ya te la quitas de la cabeza, tu mente se libera de ella y tienes hueco para otras cosas. A veces estoy deseando de escribir un post para que las ideas que me han llegado queden guardadas en algun sitio antes de que se me olviden. Así le dejan hueco a otros nuevos pensamientos y no tengo la mente dando vueltas a lo mismo, una y otra vez.

Intenta escribir tus pensamientos creativos y positivos. Las críticas negativas no te ayudan ni a ti ni a nadie, esto te lo digo despues de equivocarme muchas veces. No obstante lo mejor es que te equivoques tu mismo.

Como efectos colaterales de tener un blog (recuerda que no es el objetivo sino un efecto colateral) a veces conoces gente interesante y en los comentarios del post se producen discusiones muy enriquecedoras. Por eso recomiendo configurar plugins como el de Disqus para que las personas que comentan sean avisadas cuando alguien respnda. Otro efecto colateral es que se te pueden abrir nuevas oportunidades laborales. Te encuentra gente buscando resolver problemas sobre los que tu has escrito. Demuestra que eres una persona que se preocupa de su aprendizaje y que tiene una trayectoria de esfuerzo. Mi blog me ayudó a conseguir mi trabajo en Dublin cuando me fui sin nada, me ayudó más que tener un título universitario según me dijeron los entrevistadores. Pero recuerda! no escribas un blog porque quieres cambiar de empleo! porque no es así como funciona, solo funciona cuando sucede naturalmente. Sino te pillarán vendiendo la moto.
Otra cosa buena es que no tienes que repetirte tanto en ciertas conversaciones, puedes animar a la gente a que lea un post tuyo cuando se trata de un tema que ya estas cansado de hablar.

Para empezar hay muchos servicios gratuitos como Blogger.com o WordPress.org donde en cuestion de dos minutos tienes el blog creado. Mas adelante puedes comprarte un dominio propio e instalarte wordpress o lo que mas te guste. Si estas planteandote programar tu propio motor de blog, está bien, pero hazlo independiente de escribir tu blog, es decir que empieces escribiendo en wordpress mientras te programas tu blog y luego migres. Porque sino puede que nunca arranques a escribir.

Este post parece ser más para los demás que para mí, un poco al contrario de lo que cuento en él... sin embargo el verbalizar por escrito estos pensamientos me hace comprometerme más conmigo mismo a escribir con más frecuencia en mis dos blogs. Verbalizar tus pensamientos en voz alta o por escrito, aumenta tu compromiso contigo mismo, mucho más que si es un pensamiento interno.

Si te animas a iniciar un blog, sientete libre de dejar un comentario en este post con el enlace al mismo, yo me alegraré de saberlo.

Este post seguramente será actualizado con más pensamientos.

Among other qualities good tests should be easy to read, quick to understand. When the test requires complex data structures to be sent to the SUT or to be part of a stubbed answer, it takes longer to read. Moreover those structures use to evolve as the production code does causing too many changes in the tests in order to adapt them. An indirection level in between the test and the production code helps improve readability and ease of maintenance. Builders come to the rescue. I often overload builder methods to support several data structures and then apply the conversions internally.

As an example, this is the setup of one of our tests before the final refactor:

  1. [Test] public async void
  2. raise_multiple_choices_event_on_equipment_selection () {
  3. SetupViewModelWithMockService();
  4. var selectedEquipment = new Equipment { Code = "PR" };
  5. var oneEquipment = new Equipment { Code = "PR1"};
  6. var otherEquipment = new Equipment { Code = "PR2"};
  7. var equipments = new List<Equipment>{ selectedEquipment, oneEquipment, otherEquipment };
  8. var multipleChoices = new List<CompulsoryCombinationChoice> {
  9. new CompulsoryCombinationChoice(new List<CompulsoryCombinationItem> {
  10. new CompulsoryCombinationItem(oneEquipment.Code, CatalogItemTypes.Equipment)
  11. }),
  12. new CompulsoryCombinationChoice(new List<CompulsoryCombinationItem> {
  13. new CompulsoryCombinationItem(otherEquipment.Code, CatalogItemTypes.Equipment)
  14. })
  15. };
  16. ACatalog.MockingDependenciesOf(vm)
  17. .WithEquipments(equipments)
  18. .ResolvingCompulsoryCombinationsAs(multipleChoices)
  19. .Configure();
  20. /* act ... */
  21. /* assert ... */
  22.  

Imagine how ugly it was before the "ACatalog" builder. And this is the test after the builder was overloaded to supply a more comfortable API:

  1. [Test] public async void
  2. raise_multiple_choices_event_on_equipment_selection() {
  3. SetupViewModelWithMockService();
  4. var theEquipment = "PR";
  5. var equipment1 = "PR1";
  6. var equipment2 = "PR2";
  7. ACatalog.MockingDependenciesOf(vm)
  8. .WithEquipments(theEquipment, equipment1, equipment2)
  9. .ResolvingCompulsoryCombinationsAs(
  10. MultipleCompulsoryCombinationChoices(
  11. equipment1, equipment2))
  12. .Configure();
  13. /* act ... */
  14. /* assert ... */
  15.