Dom Finn, Lead Developer at UNiDAYS
@cleverfinn
Robust
/roh-buhst/ - adjective
Strong and effective in all or most situations and conditions
Robust
/roh-buhst/ - adjective
Strong and effective in all or most situations and conditions
Robust
/roh-buhst/ - adjective
Strong and effective in all or most situations and conditions
Robust
/roh-buhst/ - adjective
Strong and effective in all or most situations and conditions
try
{ businessCriticalThing1(); businessCriticalThing2(); } catch (BadDevelopmentPractice b) { b.sweepUnder(rug); }
Who Cares?
Accounts - Code Management
Ops - Code Management
Support - On-Site Known Issues
Support - Support Team
Support - Social Team
Legal - SLAs
Commercial Analytics - Data loss
Business - KPI Impact
Everything (else)
CAP
Side Effect 1
Side Effect 2
Side Effect 3
Action
(Combinatorics)
public ActionResult UpdateUser(UserViewModel viewModel)
{
var user = userRepository.Get(viewModel.Id);
user.Email = viewModel.email;
user.Password = viewModel.password;
emailService.SendAccountUpdatedEmail(user);
userRepository.Update(user);
reportService.RecordEvent(new UserUpdatedEvent(user));
return Redirect("/user-updated");
}
| 1) Email | 2) Update | 3) Report | |
|---|---|---|---|
| Pass | Pass | Pass | Yes |
| Pass | Pass | Fail | Yes |
| Pass | Fail | No | |
| Fail | No |
| 1) Update | 2) Report | 3) Email | |
|---|---|---|---|
| Pass | Pass | Pass | Yes |
| Pass | Pass | Fail | Yes |
| Pass | Fail | Yes | |
| Fail | No |
| 1) Email | 2) Report | 3) Update | |
|---|---|---|---|
| Pass | Pass | Pass | Yes |
| Pass | Pass | Fail | No |
| Pass | Fail | No | |
| Fail | No |
| 1) Update | 2) Email | 3) Report | |
|---|---|---|---|
| Pass | Pass | Pass | Yes |
| Pass | Pass | Fail | Yes |
| Pass | Fail | Yes | |
| Fail | No |
25%
50%
75%
75%
| 1) Report | 2) Email | 3) Update | |
|---|---|---|---|
| Pass | Pass | Pass | Yes |
| Pass | Pass | Fail | No |
| Pass | Fail | No | |
| Fail | No |
25%
| 1) Report | 2) Update | 3) Email | |
|---|---|---|---|
| Pass | Pass | Pass | Yes |
| Pass | Pass | Fail | Yes |
| Pass | Fail | No | |
| Fail | No |
50%
public ActionResult UpdateUser(UserViewModel viewModel)
{
var user = userRepository.Get(viewModel.Id);
user.Email = viewModel.email;
user.Password = viewModel.password;
+ userRepository.Update(user);
emailService.SendAccountUpdatedEmail(user);
- userRepository.Update(user);
reportService.RecordEvent(new UserUpdatedEvent(user));
return Redirect("/user-updated");
}
Requires:
Side Effect 1
Side Effect 2
Side Effect 3
Action
Action
Side Effect 1
Side Effect 2
Side Effect 3
(Immediately Consistent Path)
public ActionResult UpdateUser(UserViewModel viewModel)
{
var user = userRepository.Get(viewModel.Id);
user.Email = viewModel.email;
user.Password = viewModel.password;
userRepository.Update(user); emailService.SendAccountUpdatedEmail(user); reportService.RecordEvent(new UserUpdatedEvent(user)); return Redirect("/user-updated"); }
try { var user = userRepository.Get(resource.Id); try { userRepository.Update(user); try { emailService.SendAccountCreatedEmail(user); } catch { } try { reportService.RecordEvent(new UserUpdatedEvent(user)); } catch { } } catch { } } catch { }
*mostly
try
{
// do stuff
} catch {
// just in case lolol
}
1) Bury your head
try
{
// do stuff
} catch (Exception e) {
// no idea what will be thrown
}
2) Catch all the things
try
{
// do stuff
} catch {
throw;
}
3) Hot potato
Theres a time and a place...
(Probably not when and where you’re using them)
Cue sweeping generalisations...
Guid ConvertToGuid(string guid)
{
return Guid.Parse(guid);
}
No
Guid ConvertToGuid(string guid)
{
try
{
return Guid.Parse(guid);
}
catch
{
return Guid.Empty;
}
}
No
Guid ConvertToGuid(string guid) { Guid g; if(Guid.TryParse(guid, out g); return g; return Guid.Empty; }
Yes
try { var user = userRepository.Get(resource.Id); try { userRepository.Update(user); try { emailService.SendAccountCreatedEmail(user); } catch { } try { reportService.RecordEvent(new UserUpdatedEvent(user)); } catch { } } catch { } } catch { }
public enum ExecutionResult
{
Success = 1,
Failure = 2
}
public sealed class ExecutionResult<T>
{
public T Data;
public ExecutionResult Result;
}
User IUserRepository.Get (Guid id); void IUserRepository.Update (User user); void IEmailService.SendWelcomeEmail (User user); void IReportService.RecordEvent<TEvent> (TEvent @event);
ExecutionResult<User> IUserRepository.Get (Guid id); ExecutionResult IUserRepository.Update (User user); ExecutionResult IEmailService.SendWelcomeEmail (User user); ExecutionResult IReportService.RecordEvent<TEvent> (TEvent @event);
Before
After
var userResult = userRepository.Get(resource.Id);
if(userResult.Result == ExecutionResult.Failure)
return ErrorResult.ServiceUnavailable();
var user = userResult.Data;
var result = userRepository.Update(user);
if(userResult.Result == ExecutionResult.Failure)
return ErrorResult.ServiceUnavailable();
emailService.SendAccountUpdatedEmail(user); // ignore result status
reportService.RecordEvent(new UserUpdatedEvent(user)); // ignore result status
var thing = getThing(thingId);
// just in case, lol!
if(thing == null)
return;
thing.doThing();
var thing = getThing(thingId); var things = getThings();