Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,596
|
Comments: 51,224
Privacy Policy · Terms
filter by tags archive
time to read 2 min | 302 words

Vijay Santhanam asks about the design process I takes for building:

How much analysis/OO modeling you do before you start writing real code? With tools like R#/SVN is the up-front design pretty much dead for garage projects?

My answer to that is complex. I don't do design. I don't think that I ever had. What I would do is something I call envisioning. I have posted several such things in the past, most recently it was Architecting Twitter.

A good way of describing that is this picture:

image

I am not really interested in the entire picture. I am interested in very small key parts of it. This foundation is what the entire application will revolve around. Very rarely I'll sit and try to draw out the entire system up front. And each and every time that I tried to do that I have had good reasons to regret it.

Once I identified the shape of the system, I I can start working on it. But the final result tend to be different than even the vague shape that I had at the beginning. I routinely modify the foundation of the system for the first part of the project, until it stable enough to fit what I want it to do comfortably.

I like to use tests and real use cases as the driving forces for this. A good sample of that can be seen with Rhino Security. I envisioned the system several months months before I actually wrote that, and while many of the high level concepts were retained, a lot of the infrastructure was shifted, as I faced real world constraints and had to solve them.

time to read 1 min | 183 words

I wrote a test for this piece code:

public virtual void View([ARFetch("id")]Webcast webcast)
{
    PropertyBag["webcast"] = webcast;
}

Now, this looks like utter lunacy, isn't it? Especially when the test looks like:

[Test]
public void When_view_called_will_send_webcast_to_view()
{
	var webcast = new Webcast();
	controller.View(webcast);

	controller.PropertyBag["webcast"].ShouldBeTheSameAs(webcast);
}

Except that now that I have covered the very basic item, I now have another few tests:

[Test]
public void When_view_called_with_null_webcast_will_render_missing_webcast_view()
{
	controller.View(null);

	controller.SelectedViewName.ShouldEqual(@"Webcast\Missing");
}

[Test]
public void When_view_called_with_unpublished_webcast_will_render_unpublished_webcast_view()
{
	controller.View(new Webcast());

	controller.SelectedViewName.ShouldEqual(@"Webcast\Unpublished");
}

And the simplest thing that can make those test pass is:

public virtual void View([ARFetch("id")]Webcast webcast)
{
	if(webcast==null)
	{
		RenderView("Missing");
		return;
	}
	if(webcast.PublishDate==null)
	{
		RenderView("Unpublished");
		return;
	}
        PropertyBag["webcast"] = webcast;
}

By having tests from the beginning, even for trivial piece of code, I ensured both that the trivial case will work and that I am already starting with the right tone.

time to read 1 min | 173 words

Going back to why I hate new projects, I am trying to figure out how to perform UI level security. Right now I need to dynamically decide whatever or not a user can edit a webcast. I came up with the following options.

Explicitly allow this for administrators only:

<% component adminOnly: %>
${ Html.LinkTo( "Edit", "webcast", "edit", webcast.Id) }
<% end %>

Allow this by operation:

<% component securedBy, {@operation: '/webcast/edit': %>
${ Html.LinkTo( "Edit", "webcast", "edit", webcast.Id) }
<% end %>

The second is much more flexible, but I am not sure that I am happy about putting the operation in the view.

I can create this, for that matter, which might be better all around, in which case this encapsulate everything inside it.

<% component edit, {@entity: webcast} %>

Thoughts?

time to read 5 min | 827 words

I am trying to get BDD. As such, I started with building a list of specifications. Here is what I have so far. Now you get the chance to tell me why I am wrong...

As a user
I want to be able to see the latest webcast summary on the homepage
So that I have great visibility on what is new
-------------------
Acceptance criteria:
- Can see the name of the latest webcast
- Can see the date it was published
- Can read the short description
===============================================================
As a user
I want to be able to see the details of a webcast
So that I can see more details about the webcast
-------------------
Acceptance criteria:
- Can see the name of the latest webcast
- Can read the short description
- Can view number of downloads
- Can view relevant files
===============================================================
As a user
I want to be able to browse the archives of the site
So I can see old shows
-------------------
Acceptance criteria:
- Can see the names of all published webcasts
- Can click to the details of each webcast
- Can view number of downloads
- Can sort the list
===============================================================
As a user
I want to be able to subscribe to an RSS feed of the webcasts
So that I wouldn't have to check the site all the time
-------------------
Acceptance criteria:
- Each page on the site should have RSS link for news
- Publishing a new webcast would appear on the RSS
===============================================================
As an administor
I want to be able to add a new webcast
So that I can publish new webcasts
-------------------
Acceptance criteria:
- Can get to admin part of the site
- Can add a new webcast
- Will go through validation
- Can use rich text editor for the description
- Can set publish date
===============================================================
As an administrator
I want to be able to edit the details of an existing webcast
So that I can fix typos and change the data if I need to
-------------------
Acceptance criteria:
- Requires login
- Goes through validation
- Will update the RSS feed
===============================================================
As an administrator
I want to be able to login to the site
So that I can perform administrative functions
-------------------
Acceptance criteria:
- Requires login
- Unauthenticated users cannot login
- Non administrators cannot login
- Passwords are not kept in the clear
===============================================================

time to read 1 min | 177 words

I hate starting new projects.

I realize that this is a fairly uncommon opinion. Most developers that I have met loved going into new projects, starting from a blank slate. I dislike it, because early on in a project, there are all too many things that are still moving. There is a big amount of work that needs to be done before you can see real results.

Here is a typical graph for the time per feature.

image

The first steps are the most frustrating ones. You work for a long while before you can say that you have something that is really worth showing.

Chad Myers calls this Time to Login Screen, I like to think about this as: Time to Business Value, which is just a bit more ambitious.

To add to the burden, I am working with MonoRail after a long hiatus, and I forgot some things and missed other, so I need to ramp up on them again.

time to read 2 min | 310 words

Take a look at this test:

[Test]
public void When_asking_for_latest_webcast_will_not_consider_webcasts_published_in_the_future()
{
	var webcast = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-2) };
	With.Transaction(() => webcastRepository.Save(webcast));
	var webcast2 = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(2) };
	With.Transaction(() => webcastRepository.Save(webcast2));
	Assert.AreEqual(webcast.Id, webcastRepository.GetLatest().Id);
}

It looks like a valid test, doesn't it? It has a huge problem. It is brittle.

I just added a new non null property, and all the tests broke. I started to add the new property value to the test, before I realize what I was doing. I run into a friction point, and I was trying to cover it with code. Next time I would add such a property, I would run into the same problem.

This is unacceptable.

The standard solution for this is to create a factory for this, or use an Object Mother. This was never something that I was fond of. I always need more flexibility than I can usually get from it, and I hate building builders, that is so boring.

Turn out, I can eat the cake and keep it.

I created the following test class:

[ActiveRecord("Webcasts")]
public class TestableWebcast : Webcast
{
	public TestabbleWebcast()
	{
		Name = "Test name";
		Description = "Test description";
	}
}

And now the test change to:

[Test]
public void When_asking_for_latest_webcast_will_not_consider_webcasts_published_in_the_future()
{
	var webcast = new TestableWebcast { PublishDate = DateTime.Now.AddDays(-2) };
	With.Transaction(() => webcastRepository.Save(webcast));
	var webcast2 = new TestableWebcast { PublishDate = DateTime.Now.AddDays(2) };
	With.Transaction(() => webcastRepository.Save(webcast2));
	Assert.AreEqual(webcast.Id, webcastRepository.GetLatest().Id);
}

I get to keep the nice object initializer syntax, and I even get more clarity, since I can now specify only the properties that I am actually interested in.

The only annoying thing is that I have to define the TestableWebcast as an entity as well, but I can live with it.

time to read 2 min | 329 words

imageI have been waiting on the release for Rhino Mocks 3.5, seeing what kind of feedback I can get from users. I also had another reason, I hadn't had the chance to really give it a good testing, in the only manner that matter, using it to develop production ready software.

Here is my latest test, which I am fairly pleased with.

[TestFixture]
public class WebcastControllerTest
{ 
	private WebcastController controller;
	private IRepository<Webcast> repositoryStub;
	private StubEngineContext engineContext;
	private IUnitOfWork unitOfWorkStub;
	private IDisposable disposeGlobalUnitOfWorkRegistration;

	[SetUp]
	public void Setup()
	{
		repositoryStub = MockRepository.GenerateStub<IRepository<Webcast>>();
		unitOfWorkStub = MockRepository.GenerateStub<IUnitOfWork>();
		controller = new WebcastController(repositoryStub)
		             	{
		             		ControllerContext = new ControllerContext{Name = "webcast"},
		             	};
		engineContext = new StubEngineContext();
		controller.SetEngineContext(engineContext);
		disposeGlobalUnitOfWorkRegistration = UnitOfWork.RegisterGlobalUnitOfWork(unitOfWorkStub);
	}

	[TearDown]
	public void TearDown()
	{
		disposeGlobalUnitOfWorkRegistration.Dispose();
	}

	[Test]
	public void When_index_called_will_send_existing_webcasts_to_view()
	{
		var webcasts = new List<Webcast>();
		repositoryStub.Stub(x => x.FindAll()).Return(webcasts);

		controller.Index();

		Assert.AreSame(webcasts, controller.PropertyBag["webcasts"]);
	}

	[Test]
	public void When_display_called_will_send_webcast_to_view()
	{
		var webcast = new Webcast();
		controller.Display(webcast);

		Assert.AreSame(webcast, controller.PropertyBag["webcast"]);
	}

	[Test]
	public void When_edit_called_with_webcast_will_send_webcast_to_view()
	{
		var webcast = new Webcast();
		controller.Edit(webcast);

		Assert.AreSame(webcast, controller.PropertyBag["webcast"]);
	}

	[Test]
	public void When_saving_invalid_webcast_will_redirect_to_edit()
	{
		var webcast = new Webcast();

		controller.Validator.IsValid(webcast);
		controller.PopulateValidatorErrorSummary(webcast, controller.Validator.GetErrorSummary(webcast));

		controller.Save(webcast);

		Assert.Contains(engineContext.MockResponse.RedirectedTo, "edit");
	}

	[Test]
	public void When_saving_invalid_webcast_will_flash_error_summary_and_webcast_in_flash()
	{
		var webcast = new Webcast();

		controller.Validator.IsValid(webcast);
		controller.PopulateValidatorErrorSummary(webcast, controller.Validator.GetErrorSummary(webcast));

		controller.Save(webcast);

		Assert.AreSame(webcast, controller.Flash["webcast"]);
		Assert.AreSame(controller.Validator.GetErrorSummary(webcast), controller.Flash["errorSummary"]);
	}

	[Test]
	public void When_saving_valid_webcast_will_redirect_to_display()
	{
		var webcast = new Webcast();

		// explicitly not calling validation, the controller will assume 
		// that this is a valid object

		controller.Save(webcast);

		Assert.Contains(engineContext.MockResponse.RedirectedTo, "display");
	}

	[Test]
	public void When_saving_valid_webcast_will_save_and_flash_unit_of_work()
	{
		var webcast = new Webcast();

		// explicitly not calling validation, the controller will assume 
		// that this is a valid object

		controller.Save(webcast);

		repositoryStub.AssertWasCalled(x=>x.Save(webcast));
		unitOfWorkStub.AssertWasCalled(x=>x.TransactionalFlush());
	}
}
time to read 2 min | 365 words

Two days ago I asked how many tests this method need:

///<summary> 
///Get the latest published webcast 
///</summary>
public Webcast GetLatest();

Here is what I came up with:

[TestFixture]
public class WebcastRepositoryTest : DatabaseTestFixtureBase
{
	private IWebcastRepository webcastRepository;

	[TestFixtureSetUp]
	public void TestFixtureSetup()
	{
		IntializeNHibernateAndIoC(PersistenceFramework.ActiveRecord, 
			"windsor.boo", MappingInfo.FromAssemblyContaining<Webcast>());
	}

	[SetUp]
	public void Setup()
	{
		CurrentContext.CreateUnitOfWork();
		webcastRepository = IoC.Resolve<IWebcastRepository>();
	}

	[TearDown]
	public void Teardown()
	{
		CurrentContext.DisposeUnitOfWork();
	}

	[Test]
	public void Can_save_webcast()
	{
		var webcast = new Webcast { Name = "test", PublishDate = null };
		With.Transaction(() => webcastRepository.Save(webcast));
		Assert.AreNotEqual(0, webcast.Id);
	}

	[Test]
	public void Can_load_webcast()
	{
		var webcast = new Webcast { Name = "test", PublishDate = null };
		With.Transaction(() => webcastRepository.Save(webcast));
		UnitOfWork.CurrentSession.Evict(webcast);

		var webcast2 = webcastRepository.Get(webcast.Id);
		Assert.AreEqual(webcast.Id, webcast2.Id);
		Assert.AreEqual("test", webcast2.Name);
		Assert.IsNull(webcast2.PublishDate);
	}

	[Test]
	public void When_asking_for_latest_webcast_will_not_consider_any_that_is_not_published()
	{
		var webcast = new Webcast { Name = "test", PublishDate = null };
		With.Transaction(() => webcastRepository.Save(webcast));

		Assert.IsNull(webcastRepository.GetLatest());
	}

	[Test]
	public void When_asking_for_latest_webcast_will_get_published_webcast()
	{
		var webcast = new Webcast { Name = "test", PublishDate = null };
		With.Transaction(() => webcastRepository.Save(webcast));
		var webcast2 = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-1) };
		With.Transaction(() => webcastRepository.Save(webcast2));

		Assert.AreEqual(webcast2.Id, webcastRepository.GetLatest().Id);
	}

	[Test]
	public void When_asking_for_latest_webcast_will_get_the_latest_webcast()
	{
		var webcast = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-2) };
		With.Transaction(() => webcastRepository.Save(webcast));
		var webcast2 = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-1) };
		With.Transaction(() => webcastRepository.Save(webcast2));

		Assert.AreEqual(webcast2.Id, webcastRepository.GetLatest().Id);
	}

	[Test]
	public void When_asking_for_latest_webcast_will_not_consider_webcasts_published_in_the_future()
	{
		var webcast = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-2) };
		With.Transaction(() => webcastRepository.Save(webcast));
		var webcast2 = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(2) };
		With.Transaction(() => webcastRepository.Save(webcast2));
		Assert.AreEqual(webcast.Id, webcastRepository.GetLatest().Id);
	}
}

And the implementation:

public class WebcastRepository : RepositoryDecorator<Webcast>, IWebcastRepository
{
	public WebcastRepository(IRepository<Webcast> repository)
	{
		Inner = repository;
	}

	public Webcast GetLatest()
	{
		var publishedWebcastsByDateDesc =
			from webcast in Webcasts
			where webcast.PublishDate != null && webcast.PublishDate < SystemTime.Now()
			orderby webcast.PublishDate descending 
			select webcast;

		return publishedWebcastsByDateDesc.FirstOrDefault();
	}

	private static IOrderedQueryable<Webcast> Webcasts
	{
		get { return UnitOfWork.CurrentSession.Linq<Webcast>(); }
	}
}

I think it is pretty sweet.

I hate IIS today

time to read 1 min | 74 words

I am trying to execute the following piece of code:

Microsoft.Web.Administration.WebConfigurationManager.GetSection("system.webServer/httpHandlers");

Of course, this fails with a completely bizarre exception.

image

For some reason, I have the feeling it is not supposed to do that. Trying to explicitly pass the path where web.config lies also fails. When I finally made it use the correct path, it still gave the same error.

Urgh!

FUTURE POSTS

  1. Replacing developers with GPUs - about one day from now
  2. Memory optimizations to reduce CPU costs - 6 days from now
  3. AI's hidden state in the execution stack - 9 days from now

There are posts all the way to Aug 18, 2025

RECENT SERIES

  1. RavenDB 7.1 (7):
    11 Jul 2025 - The Gen AI release
  2. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
  3. Webinar (7):
    05 Jun 2025 - Think inside the database
  4. Recording (16):
    29 May 2025 - RavenDB's Upcoming Optimizations Deep Dive
  5. RavenDB News (2):
    02 May 2025 - May 2025
View all series

Syndication

Main feed ... ...
Comments feed   ... ...
}