Blog on Bravo LT | Learning & Technology
@@ -26,7 +26,7 @@
Fri, 12 Jul 2019 12:11:29 +0600
//www.bravolt.com/blog/bravos-ribbon-cutting-open-house/
- Save the Date##### You’re invited to Bravo LT’s Ribbon Cutting & Open House Thursday, August 15th, 3pm - 5pm
+ Save the Date You’re invited to Bravo LT’s Ribbon Cutting & Open House Thursday, August 15th, 3pm - 5pm
Join us as we celebrate our recent move downtown. Ribbon Cutting at 4pm.
We can’t wait to meet our neighbors and catch up with clients and friends!
Snacks and drinks provided.
@@ -49,7 +49,7 @@ The free event will be held on August 12 & 13, 2019 at the Bravo office
Wed, 29 May 2019 12:11:29 +0600
//www.bravolt.com/blog/we-moved-downtown-grand-rapids/
- Our New Location##### Bravo has officially moved downtown Grand Rapids! Last month we said goodbye to our Cascade Parkway location and (after a few construction projects and a bit of cosmetic work in the new space) hello to Monroe Center. Our official signage is in the works, but in the meantime, we are the dark gray building located at the corner of Division and Monroe Center. More specifically:
+ Our New Location Bravo has officially moved downtown Grand Rapids! Last month we said goodbye to our Cascade Parkway location and (after a few construction projects and a bit of cosmetic work in the new space) hello to Monroe Center. Our official signage is in the works, but in the meantime, we are the dark gray building located at the corner of Division and Monroe Center. More specifically:
Bravo LT, 40 Monroe Center NW, Suite 11, Grand Rapids, MI 49503
@@ -121,8 +121,7 @@ The free event will be held on September 11 & 12, 2018 at Calvin College
//www.bravolt.com/blog/agile-in-grand-rapids/
Equip Your Teams with Agile We’re passionate about equipping your teams with the cutting-edge Lean Agile Certification training they need!
-This summer we hosted an Agile Advanced Scrum Master Certification course in down-town Grand Rapids. We had a blast hosting and teaching participants from Gordon Food Service, Meijer, Spectrum Health, and more. Most beneficially, great discussions formed across the varying industries which created a learning environment filled with collaboration.
-During the two day certification course we covered such things as:
+This summer we hosted an Agile Advanced Scrum Master Certification course in down-town Grand Rapids. We had a blast hosting and teaching participants from Gordon Food Service, Meijer, Spectrum Health, and more. Most beneficially, great discussions formed across the varying industries which created a learning environment filled with collaboration.
-
@@ -141,8 +140,7 @@ Google brought a lot to the table through their livestream event. Many were amaz
Fri, 22 Sep 2017 12:11:29 +0600
//www.bravolt.com/blog/2017-raspberry-pi-youth-camp-recap/
- At Bravo LT, we believe STEM (Science, Technology, Engineering, Math) education is key to strengthening the future for the next generation. As a technology and e-Learning company in West Michigan, we love introducing the next generation of children to technology. Last month we hosted our third-annual Raspberry Pi Youth Camp which gave local students (ages 10-14) an opportunity to explore computer programming in a fun and engaging way.
- We hosted this free event at Davenport University in Grand Rapids.
+ At Bravo LT, we believe STEM (Science, Technology, Engineering, Math) education is key to strengthening the future for the next generation. As a technology and e-Learning company in West Michigan, we love introducing the next generation of children to technology. Last month we hosted our third-annual Raspberry Pi Youth Camp which gave local students (ages 10-14) an opportunity to explore computer programming in a fun and engaging way.
-
@@ -239,7 +237,8 @@ The free event, held August 9 and 11, 2016 at Davenport University in Grand Rapi
//www.bravolt.com/blog/bravo-open-source-symposium/
Bravo Open Source Symposium October 26, November 2, 9, and 16 5:00 - 7:00 PM
Event Description: BOSS 2015 explores specific open source topics, to include:
- Data Grids: To Infinispan and Beyond (Day 1) Introduction to Docker (Day 2) Maven for Power Users (Day 3) Introduction to Clojure (Day 4) This free four-day symposium, held on alternating Mondays in September and October, features highly interactive training sessions to include the following delivery methods: traditional lecture and demonstration, video, case studies, hands-on activities and group discussion.
+ Data Grids: To Infinispan and Beyond (Day 1) Introduction to Docker (Day 2) Maven for Power Users (Day 3) Introduction to Clojure (Day 4)
+This free four-day symposium, held on alternating Mondays in September and October, features highly interactive training sessions to include the following delivery methods: traditional lecture and demonstration, video, case studies, hands-on activities and group discussion.
diff --git a/public/blog/nullable-reference-types/index.html b/public/blog/nullable-reference-types/index.html
index c3af5b5..9775163 100644
--- a/public/blog/nullable-reference-types/index.html
+++ b/public/blog/nullable-reference-types/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -149,285 +149,445 @@ Getting Started with Nullable Reference Types in C# 8
- The next version of the C# language, C# 8, is scheduled to ship in September 2019 along with .NET Core 3.
+
+
+
The next version of the C# language, C# 8, is scheduled to ship in September 2019 along with .NET Core 3.
C# 8 brings some pretty cool features to the language.
One of the features I’m most excited about is Nullable Reference Types, which enables developers to prevent many of the problems that often come along with null values.
+
C# 8 and .NET Core 3 are already in public preview, so you can give them a whirl right now.
In this post, I’m going to walk you through a simple example to demonstrate how to take advantage of Nullable Reference Types.
+
But first, a bit of background.
+
Value types and reference types
+
Data types in C# fall into two broad categories: value types and reference types.
+
Value types are the simpler kind: integers, Booleans, characters, and so forth.
When you create a variable of a value type, you can think of the variable as “holding” the value (disclaimer: this is a mental model, not a robust description of your computer’s memory).
+
For instance, the statement
-creates the variable x
of type int
and gives it the value 4
.
+
+
int x = 4;
+
+
+creates the variable x
of type int
and gives it the value 4
.
If you were to do something to x
, say,
-then x
would hold the value 5
and the value 4
would be gone.
+
+x++;
+
+
+then x
would hold the value 5
and the value 4
would be gone.
+
Reference types are the more complicated kind: objects, strings, etc.
When you create a variable of a reference type, the variable doesn’t so much “hold” the value as “point to” (or “reference”) the value.
+
For instance, the statement
-Person p = new Person { FirstName = "Bowser", LastName = "Castle" };
-
creates the variable p
of type Person
, places the object data somewhere in memory, and gives p
the location of that data.
+
+Person p = new Person { FirstName = "Bowser", LastName = "Castle" };
+
+
+creates the variable p
of type Person
, places the object data somewhere in memory, and gives p
the location of that data.
+
If you were to do something to a property of p
, say,
-p
would still hold the same value: a reference to the object.
+
+
p.Age = 62;
+
+
+p
would still hold the same value: a reference to the object.
Only the object data would change.
+
On the other hand, if you were to do something to p
itself, say,
-p = new Person { FirstName = "Ganon", LastName = "Tower" };
-
a new object would be created in memory (Ganon Tower) and p
would be updated with the location of the new object.
+
+
p = new Person { FirstName = "Ganon", LastName = "Tower" };
+
+
+a new object would be created in memory (Ganon Tower) and p
would be updated with the location of the new object.
The old object (Bowser Castle) would be left hanging around in memory with no variable pointing to it - at least until the plumber garbage collector got around to cleaning it up.
+
The way that reference types work - pointing to the data rather than holding the data - has a strange consequence: a reference type variable can point to nothing.
+
Null values
+
In C#, value types cannot be null unless you explicitly declare a variable as nullable.
A statement like
-will not compile.
+
+
bool b = null;
+
+
+will not compile.
There is no such thing as a bool
with no value - it has to be either true or false.
In fact, if you fail to provide a value, you’ll get a sensible default.
A declaration like
-will give b
the value false
, which is the default for Booleans.
+
+bool b;
+
+
+will give b
the value false
, which is the default for Booleans.
+
Usually, this is a good thing.
In the off chance that you want a nullable value type, you can declare it with a question mark:
-Reference types, on the other hand, are nullable right from the start.
+
+
bool? b = null;
+
+
+Reference types, on the other hand, are nullable right from the start.
Let’s make that same traditional declaration with a reference type:
-We now have a string
type variable called s
, but we haven’t yet created a real string
object in memory, so s
points to nothing.
+
+
string s;
+
+
+We now have a string
type variable called s
, but we haven’t yet created a real string
object in memory, so s
points to nothing.
This is represented by s
getting the value null
.
+
Oddly, even though s
does not point to an actual string, the compiler allows you to write expressions like s.Length
.
This throws a NullReferenceException
because you are attempting to dereference a null reference.
In other words, you are trying to access a member of an object that doesn’t exist, so your program blows up.
+
All the compiler knows is that s
is of type string
, and objects of type string
have a Length
property.
Unfortunately, in this case, we have no object, and thus no Length
property.
+
I won’t go into why languages like C# allow null references in the first place, partly because it’s beyond the scope of this post, but also because the lead designer of C# wrote an excellent article that covers that very topic.
I would recommend it if you’re interested in the design rationale behind Nullable Reference Types (which we really will be getting to in just a moment).
+
For now, all I hope to demonstrate is that we have a conundrum: reference type variables can be null, but the compiler doesn’t have a good way to help us to deal with those nulls responsibly.
We are human beings and sometimes we are irresponsible, so we end up with a NullReferenceException
at runtime.
+
Nullable Reference Types
+
To solve this problem, C# 8 introduces a feature called “Nullable Reference Types”.
The feature has two main parts that work together: some familiar syntax you can use to declare which reference types should be nullable and some compiler warnings to help you to follow through on that intent.
+
Let’s see it in action.
If you’d like to paint along with us at home, here are the tools we’ll be using (all of which are cross-platform):
+
+
To begin, we’ll open a terminal, create an empty directory, and scaffold a new .NET Core console app:
-mkdir nullable-sample
+
+mkdir nullable-sample
cd nullable-sample
dotnet new console
-
There are two new files in our directory.
+
+
+
There are two new files in our directory.
We’ll run code .
to inspect them in VS Code.
+
The project file is nullable-sample.csproj
:
-<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>netcoreapp3.0</TargetFramework>
- <RootNamespace>nullable_sample</RootNamespace>
- </PropertyGroup>
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>netcoreapp3.0</TargetFramework>
+ <RootNamespace>nullable_sample</RootNamespace>
+ </PropertyGroup>
+
+</Project>
+
-</Project>
-
The main program is Program.cs
:
-using System;
+The main program is Program.cs
:
-namespace nullable_sample
+using System;
+
+namespace nullable_sample
{
- class Program
+ class Program
{
- static void Main(string[] args)
+ static void Main(string[] args)
{
- Console.WriteLine("Hello World!");
+ Console.WriteLine("Hello World!");
}
}
}
-
As you can see, dotnet new console
created a simple “hello world” app.
+
+
+
As you can see, dotnet new console
created a simple “hello world” app.
Let’s build it and run it, just to get a baseline of the output we expect.
+
dotnet build
compiles the code and shows us if it found any problems:
-Build succeeded.
+
+Build succeeded.
0 Warning(s)
0 Error(s)
-
dotnet run
both compiles and runs the code, displaying the program’s console output:
-Now that we have a running sample, let’s add some code.
+
+
+dotnet run
both compiles and runs the code, displaying the program’s console output:
+
+Hello World!
+
+
+Now that we have a running sample, let’s add some code.
+
To demonstrate handling null references, we’ll start by defining a Person
class to represent someone’s full name.
-public class Person
+
+public class Person
{
- public string FirstName { get; set; }
- public string MiddleName { get; set; }
- public string LastName { get; set; }
+ public string FirstName { get; set; }
+ public string MiddleName { get; set; }
+ public string LastName { get; set; }
}
-
Many people have at least a given name and a surname, but middle names are not common in all cultures, and even where they are common, people do not always provide their middle name.
+
+
+
Many people have at least a given name and a surname, but middle names are not common in all cultures, and even where they are common, people do not always provide their middle name.
If we have a database of people, it might have a lot of nulls in the middle name column.
Let’s create a mock data access class to pretend like we’re retrieving the first record we find in a database full of people.
-public class PersonStore
+
+public class PersonStore
{
- public static Person GetFirstPerson()
- => new Person { FirstName = "Pat", LastName = "McTest" };
+ public static Person GetFirstPerson()
+ => new Person { FirstName = "Pat", LastName = "McTest" };
}
-
Now we can use this data access code in our main program to do something interesting with a Person
object, like counting the number of characters in the person’s full name and displaying it to the console.
-static void Main(string[] args)
+
+
+
Now we can use this data access code in our main program to do something interesting with a Person
object, like counting the number of characters in the person’s full name and displaying it to the console.
+
+
static void Main(string[] args)
{
Person p = PersonStore.GetFirstPerson();
- int letterCount =
+ int letterCount =
p.FirstName.Length +
p.MiddleName.Length +
p.LastName.Length;
- Console.WriteLine($"Hello, {p.FirstName}");
- Console.WriteLine($"You have {letterCount} letters in your full name");
+ Console.WriteLine($"Hello, {p.FirstName}");
+ Console.WriteLine($"You have {letterCount} letters in your full name");
}
-
That seems easy enough.
+
+
+
That seems easy enough.
However, there is a sneaky bug lurking in this code.
Before we address it, let’s see how our program behaves.
+
The code compiles without issue.
The output of dotnet build
is no different than before:
-Build succeeded.
+
+Build succeeded.
0 Warning(s)
0 Error(s)
-
When we run the code, however, we encounter a problem.
+
+
+
When we run the code, however, we encounter a problem.
Here is the output of dotnet run
:
-Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
-
The problem here is that we neglected to check for null before accessing the Length
property of p.MiddleName
.
+
+
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
+
+
+The problem here is that we neglected to check for null before accessing the Length
property of p.MiddleName
.
Our mock person record lacks a middle name, so at runtime, p.MiddlName
was null.
Let’s sweep this bug under the rug by giving Pat a middle initial.
I’m feeling lazy today and I need to ship this code.
-public static Person GetFirstPerson()
- => new Person { FirstName = "Pat", MiddleName = "Q", LastName = "McTest" };
-
If we run the code, it works! Hooray!
-Hello, Pat
+
+public static Person GetFirstPerson()
+ => new Person { FirstName = "Pat", MiddleName = "Q", LastName = "McTest" };
+
+
+If we run the code, it works! Hooray!
+
+Hello, Pat
You have 10 letters in your full name
-
Unfortunately, we now have a more insidious problem.
+
+
+
Unfortunately, we now have a more insidious problem.
Our code works fine with our test data, but what will happen when we load a record from a real database and the middle name is missing?
+
We could uncover the bug by adding more tests - and indeed, we absolutely should - but wouldn’t it be great if the compiler could help us fix it right now?
+
Let’s enable the Nullable Reference Types feature.
To do this, we have to add two lines to our project file: one to specify that we’re using C# 8 and one to turn on the feature.
-<LangVersion>8.0</LangVersion>
-<NullableContextOptions>enable</NullableContextOptions>
-
Just to see that in context, here is our modified nullable-sample.csproj
file:
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>netcoreapp3.0</TargetFramework>
- <RootNamespace>nullable_sample</RootNamespace>
- <LangVersion>8.0</LangVersion>
- <NullableContextOptions>enable</NullableContextOptions>
- </PropertyGroup>
-
-</Project>
-
With the feature enabled, let’s try to compile our code again. Here is the output of dotnet build
:
-Build succeeded.
-
-Program.cs(23,23): warning CS8618: Non-nullable property 'FirstName' is uninitialized. [/home/ed/source/nullable-sample/nullable-sample.csproj]
-Program.cs(24,23): warning CS8618: Non-nullable property 'MiddleName' is uninitialized. [/home/ed/source/nullable-sample/nullable-sample.csproj]
-Program.cs(25,23): warning CS8618: Non-nullable property 'LastName' is uninitialized. [/home/ed/source/nullable-sample/nullable-sample.csproj]
+
+<LangVersion>8.0</LangVersion>
+<NullableContextOptions>enable</NullableContextOptions>
+
+
+Just to see that in context, here is our modified nullable-sample.csproj
file:
+
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>netcoreapp3.0</TargetFramework>
+ <RootNamespace>nullable_sample</RootNamespace>
+ <LangVersion>8.0</LangVersion>
+ <NullableContextOptions>enable</NullableContextOptions>
+ </PropertyGroup>
+
+</Project>
+
+
+With the feature enabled, let’s try to compile our code again. Here is the output of dotnet build
:
+
+Build succeeded.
+
+Program.cs(23,23): warning CS8618: Non-nullable property 'FirstName' is uninitialized. [/home/ed/source/nullable-sample/nullable-sample.csproj]
+Program.cs(24,23): warning CS8618: Non-nullable property 'MiddleName' is uninitialized. [/home/ed/source/nullable-sample/nullable-sample.csproj]
+Program.cs(25,23): warning CS8618: Non-nullable property 'LastName' is uninitialized. [/home/ed/source/nullable-sample/nullable-sample.csproj]
3 Warning(s)
0 Error(s)
-
The build succeeded, just as before, but now we have some warnings. Progress!
+
+
+The build succeeded, just as before, but now we have some warnings. Progress!
+
The compiler is telling us two things:
+
- The properties of our
Person
class are all considered “non-nullable”
- Given that they’re non-nullable, they should be initialized
+
Wait a minute.
All three properties of Person
are strings.
Strings are nullable, right?
+
Yes, it is just as possible as ever to assign null
to a string.
The warnings we received are speaking about our intent.
When the Nullable Reference Types feature is enabled, the compiler considers reference types to be non-nullable by default, just like value types.
If you want a property to be nullable, you have to declare it that way.
+
Let’s say that our application requires each person to have a first and last name but makes the middle name optional.
This means that FirstName
and LastName
should be initialized to ensure they always have a value.
The empty string would be a sensible default.
-public class Person
+
+public class Person
{
- public string FirstName { get; set; } = string.Empty;
- public string MiddleName { get; set; }
- public string LastName { get; set; } = string.Empty;
+ public string FirstName { get; set; } = string.Empty;
+ public string MiddleName { get; set; }
+ public string LastName { get; set; } = string.Empty;
}
-
dotnet build
now generates only one warning:
-Build succeeded.
+
+
+
dotnet build
now generates only one warning:
-Program.cs(24,23): warning CS8618: Non-nullable property 'MiddleName' is uninitialized. [/home/ed/source/nullable-sample/nullable-sample.csproj]
+
Build succeeded.
+
+Program.cs(24,23): warning CS8618: Non-nullable property 'MiddleName' is uninitialized. [/home/ed/source/nullable-sample/nullable-sample.csproj]
1 Warning(s)
0 Error(s)
-
Because MiddleName
is optional in our application, instead of initializing it, we’ll mark it as nullable.
+
+
+
Because MiddleName
is optional in our application, instead of initializing it, we’ll mark it as nullable.
We do this by appending a question mark to the type, which is the same syntax we used earlier to make a value type nullable.
-public string? MiddleName { get; set; }
-
Now we have a more expressive domain model.
+
+
public string? MiddleName { get; set; }
+
+
+Now we have a more expressive domain model.
FirstName
and LastName
are required, so we initialize them.
MiddleName
is optional, so we mark it as nullable.
The code looks better already.
+
Let’s see how it compiles.
-Build succeeded.
+
+Build succeeded.
Program.cs(13,17): warning CS8602: Dereference of a possibly null reference. [/home/ed/source/nullable-sample/nullable-sample.csproj]
1 Warning(s)
0 Error(s)
-
Now we have a new warning.
+
+
+
Now we have a new warning.
“Dereference of a possibly null reference” means that we are accessing a member of an object that might be null, and we haven’t responsibly checked to make sure it isn’t null.
Evidently, the culprit is on line 13.
-Person p = PersonStore.GetFirstPerson();
-int letterCount =
+Person p = PersonStore.GetFirstPerson();
+
+int letterCount =
p.FirstName.Length +
p.MiddleName.Length +
p.LastName.Length;
-
Line 13 contains p.MiddleName.Length
.
+
+
+
Line 13 contains p.MiddleName.Length
.
This was the line that generated the NullReferenceException
earlier, but now we’re getting a helpful warning about it.
Because we marked MiddleName
as nullable, we have to check to make sure it isn’t null before accessing any of its members, in this case its Length
property.
+
Let’s add a null check (which we really should have done from the very beginning, had we not been in such a hurry).
-int letterCount =
+
+int letterCount =
p.FirstName.Length +
- (p.MiddleName?.Length ?? 0) +
+ (p.MiddleName?.Length ?? 0) +
p.LastName.Length;
-
Now our code builds without warnings:
-Build succeeded.
+
+
+
Now our code builds without warnings:
+
+
Build succeeded.
0 Warning(s)
0 Error(s)
-
The warning went away because the compiler is capable of analyzing the control flow of a program and determining where null values may occur.
+
+
+
The warning went away because the compiler is capable of analyzing the control flow of a program and determining where null values may occur.
By checking to make sure that p.MiddleName
was non-null before accessing its Length
property, we soothed the compiler’s fears.
+
Our program runs successfully:
-Hello, Pat
+
+Hello, Pat
You have 10 letters in your full name
-
But is the bug really fixed?
+
+
+
But is the bug really fixed?
Recall that earlier, we added a middle name to our test person in order to prevent a NullReferenceException
.
Let’s remove that test middle name and find out if our code has actually improved.
-public static Person GetFirstPerson()
- => new Person { FirstName = "Pat", LastName = "McTest" };
-
The moment of truth. dotnet run
:
-Hello, Pat
+
+public static Person GetFirstPerson()
+ => new Person { FirstName = "Pat", LastName = "McTest" };
+
+
+The moment of truth. dotnet run
:
+
+Hello, Pat
You have 9 letters in your full name
-
Success!
+
+
+
Success!
Even with a null value in our test data, our program ran safely.
+
Note that we converted our buggy code to null-safe code simply by following the indications provided to us by the compiler.
This is the real value of the Nullable Reference Types feature: it helps you write safer and more expressive code.
+
Recap
+
You can try out C# 8 with Nullable Reference Types right now.
However, keep in mind that .NET Core 3 still in preview as of the time of writing, so you might not want to use it for production code until the first stable release.
+
First, download the latest preview release of .NET Core 3.
+
Second, specify .NET Core 3, C# 8, and the “nullable context options” properties in your .csproj
file:
-<TargetFramework>netcoreapp3.0</TargetFramework>
-<LangVersion>8.0</LangVersion>
-<NullableContextOptions>enable</NullableContextOptions>
-
Third, explicitly indicate that a reference type should be allowed to carry null values by appending a question mark to the type:
-public string? MiddleName { get; set; }
-
And finally, pay attention to the warnings you get from dotnet build
and use them as a guide to improving your code.
+
+<TargetFramework>netcoreapp3.0</TargetFramework>
+<LangVersion>8.0</LangVersion>
+<NullableContextOptions>enable</NullableContextOptions>
+
+
+Third, explicitly indicate that a reference type should be allowed to carry null values by appending a question mark to the type:
+
+public string? MiddleName { get; set; }
+
+
+And finally, pay attention to the warnings you get from dotnet build
and use them as a guide to improving your code.
+
If you’d like to see the full example app we walked through in this post, it’s available on GitHub.
+
Going further
+
By design, the Nullable Reference Types feature generates warnings, not errors.
This is so that you can start fixing problems in existing code without spilling noisy red ink all over the place.
+
If you do want actual errors that fail the build, you can choose to treat all warnings as errors.
This is an existing feature in current versions of .NET (not just in .NET Core 3), and it can be enabled by adding one more property to a PropertyGroup
in your .csproj
file:
-<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-
We covered the basics of Nullable Reference Types in this post, but there is even more to the feature.
+
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+
+
+We covered the basics of Nullable Reference Types in this post, but there is even more to the feature.
If you’d like to dig deeper, check out these articles:
+
- Introducing Nullable Reference Types in C# by Mads Torgerson
- Microsoft’s official guide to Nullable Reference Types
diff --git a/public/blog/page/2/index.html b/public/blog/page/2/index.html
index 8388f03..bc5e623 100644
--- a/public/blog/page/2/index.html
+++ b/public/blog/page/2/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -310,8 +310,7 @@
Equip Your Teams with Agile We’re passionate about equipping your teams with the cutting-edge Lean Agile Certification training they need!
-This summer we hosted an Agile Advanced Scrum Master Certification course in down-town Grand Rapids. We had a blast hosting and teaching participants from Gordon Food Service, Meijer, Spectrum Health, and more. Most beneficially, great discussions formed across the varying industries which created a learning environment filled with collaboration.
-During the two day certification course we covered such things as:
+This summer we hosted an Agile Advanced Scrum Master Certification course in down-town Grand Rapids. We had a blast hosting and teaching participants from Gordon Food Service, Meijer, Spectrum Health, and more. Most beneficially, great discussions formed across the varying industries which created a learning environment filled with collaboration.
Read more
diff --git a/public/blog/page/3/index.html b/public/blog/page/3/index.html
index 1ddbf78..d0a52fd 100644
--- a/public/blog/page/3/index.html
+++ b/public/blog/page/3/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -180,8 +180,7 @@
-
At Bravo LT, we believe STEM (Science, Technology, Engineering, Math) education is key to strengthening the future for the next generation. As a technology and e-Learning company in West Michigan, we love introducing the next generation of children to technology. Last month we hosted our third-annual Raspberry Pi Youth Camp which gave local students (ages 10-14) an opportunity to explore computer programming in a fun and engaging way.
- We hosted this free event at Davenport University in Grand Rapids.
+
At Bravo LT, we believe STEM (Science, Technology, Engineering, Math) education is key to strengthening the future for the next generation. As a technology and e-Learning company in West Michigan, we love introducing the next generation of children to technology. Last month we hosted our third-annual Raspberry Pi Youth Camp which gave local students (ages 10-14) an opportunity to explore computer programming in a fun and engaging way.
Read more
diff --git a/public/blog/page/4/index.html b/public/blog/page/4/index.html
index faa5c00..0a15df7 100644
--- a/public/blog/page/4/index.html
+++ b/public/blog/page/4/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -308,7 +308,8 @@
Bravo Open Source Symposium October 26, November 2, 9, and 16 5:00 - 7:00 PM
Event Description: BOSS 2015 explores specific open source topics, to include:
- Data Grids: To Infinispan and Beyond (Day 1) Introduction to Docker (Day 2) Maven for Power Users (Day 3) Introduction to Clojure (Day 4) This free four-day symposium, held on alternating Mondays in September and October, features highly interactive training sessions to include the following delivery methods: traditional lecture and demonstration, video, case studies, hands-on activities and group discussion.
+ Data Grids: To Infinispan and Beyond (Day 1) Introduction to Docker (Day 2) Maven for Power Users (Day 3) Introduction to Clojure (Day 4)
+This free four-day symposium, held on alternating Mondays in September and October, features highly interactive training sessions to include the following delivery methods: traditional lecture and demonstration, video, case studies, hands-on activities and group discussion.
Read more
diff --git a/public/blog/pistons-vs-cavs/index.html b/public/blog/pistons-vs-cavs/index.html
index c28daf3..36166fd 100644
--- a/public/blog/pistons-vs-cavs/index.html
+++ b/public/blog/pistons-vs-cavs/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -149,8 +149,12 @@ Pistons vs Cavs
- Friday Night Road Trip!
+
+
+Friday Night Road Trip!
+
A few of us from #TeamBravo headed south on Friday night, to the Breslin Center at MSU, to see the Pistons and Cavs face off! Since they’ve both lived in Ohio, Ben and Matthew are die-hard Cleveland fans. Ed and Tom cheered for the Pistons which made for a night of light-hearted rivalry between our cheering section of four.
+
Not only do all of us enjoy working together as a collaborative and united team – we have a heck of a lot of fun outside the office. We make a point to plan lively activities for the team on the regular. This summer about 20 of us from #TeamBravo took in a White Caps game with our spouses and friends. This fall and winter we’re looking forward to 80’s Night, our office Christmas party, and any impromptu fun that pops up between now and then.
diff --git a/public/blog/raspberry-pi-computer-programming-camp-is-back-2018/index.html b/public/blog/raspberry-pi-computer-programming-camp-is-back-2018/index.html
index f685c61..295a346 100644
--- a/public/blog/raspberry-pi-computer-programming-camp-is-back-2018/index.html
+++ b/public/blog/raspberry-pi-computer-programming-camp-is-back-2018/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -150,9 +150,13 @@ Raspberry Pi Youth Computer Programming Camp is Back!
At Bravo LT, we believe STEM education is key to strengthening West Michigan from the inside out. We’re excited to host another session of Raspberry Pi Youth Camp, this fall, which will give local students (ages 12-15) an opportunity to explore computer programming in a fun and engaging way.
+
The free event will be held on September 11 & 12, 2018 at Calvin College in Grand Rapids from 5pm – 8pm. The event is powered by our developers, who generously volunteer their time to lead camp. This session of camp will focus in on the Python programming language. Youth participants will explore exciting, hands-on projects and Raspberry Pi devices — inexpensive credit card-sized computers that plug into TVs or monitors and use keyboards and mice — to learn programming.
+
Registration filled up quickly for camp. Reach out to us if you’d like your son or daughter put on the wait-list.
-If you’re a company, family, or individual who believes in inspiring students within STEM fields and bridging the gender and diversity gap in the tech sector — we invite you to support Pi Camp by becoming a Champion of Technology for Children, today! Email info@bravolt.com to learn about how you can support students with your donation to camp.
+
+If you’re a company, family, or individual who believes in inspiring students within STEM fields and bridging the gender and diversity gap in the tech sector — we invite you to support Pi Camp by becoming a Champion of Technology for Children, today! Email info@bravolt.com to learn about how you can support students with your donation to camp.
+
Be sure to check out all the fun we had at our summer session of camp:
![Raspberry Pi Youth Computer Camp, Youth Computer Programming Camp Grand Rapids]
diff --git a/public/blog/raspberry-pi-computer-programming-this-august-2019/index.html b/public/blog/raspberry-pi-computer-programming-this-august-2019/index.html
index cd0ea19..4320483 100644
--- a/public/blog/raspberry-pi-computer-programming-this-august-2019/index.html
+++ b/public/blog/raspberry-pi-computer-programming-this-august-2019/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -150,9 +150,13 @@ Youth Pi Camp this August
At Bravo LT, we believe STEM education is key to strengthening West Michigan from the inside out. We’re excited to host another session of Raspberry Pi Youth Camp, this summer, which will give local students (ages 12-15) an opportunity to explore computer programming in a fun and engaging way.
+
The free event will be held on August 12 & 13, 2019 at the Bravo office in Grand Rapids from 5:30pm – 8pm. The event is powered by our developers, who generously volunteer their time to lead camp. This session of camp will focus on the Python programming language. Youth participants will explore exciting, hands-on projects and Raspberry Pi devices — inexpensive credit card-sized computers that plug into TVs or monitors and use keyboards and mice — to learn programming.
+
Registration filled up quickly for camp but we have a few spots left: Register Here
-If you’re a company, family, or individual who believes in inspiring students within STEM fields and bridging the gender and diversity gap in the tech sector — we invite you to support Pi Camp by becoming a Champion of Technology for Children, today! Email info@bravolt.com to learn about how you can support students with your donation to camp.
+
+If you’re a company, family, or individual who believes in inspiring students within STEM fields and bridging the gender and diversity gap in the tech sector — we invite you to support Pi Camp by becoming a Champion of Technology for Children, today! Email info@bravolt.com to learn about how you can support students with your donation to camp.
+
Be sure to check out all the fun we have at camp:
Raspberry Pi Youth Computer Camp
diff --git a/public/blog/responsive-e-learning-one-size-does-not-fit-all/index.html b/public/blog/responsive-e-learning-one-size-does-not-fit-all/index.html
index 692a892..f27236c 100644
--- a/public/blog/responsive-e-learning-one-size-does-not-fit-all/index.html
+++ b/public/blog/responsive-e-learning-one-size-does-not-fit-all/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -150,11 +150,16 @@ Responsive e-Learning - One Size Does Not Fit All
e-Learning modules have traditionally been static—stiff, heavy courses that are not quite optimized for modern-day mobile devices. Designed in a constrained aspect ratio, these modules can technically fit on any size screen, but they are certainly not optimized to fit well—and that’s hardly the best option when you need to train employees on the go, or simply need to train them on a device other than a desktop computer or laptop.
+
Today, mobile is king. People across the globe heavily rely on their smartphones and tablets for everything—communicating with friends and family, reading the news, playing games, and, of course, learning! That’s where responsive training comes in.
+
Inspired by responsive web design, which targets design and development to a user’s behavior and device, responsive mobile learning (m-learning) consists of modules that are designed to “respond” to the individual devices they are being viewed on. So, how does that work? By using mobile-friendly e-learning authoring tools (we love Adapt!), our instructional designers here at Bravo LT can create modules that detect the kind of device they’re being viewed on and automatically reorganize themselves to perfectly fit on the screens of mobile devices. Plus, these courses use HTML5 instead of Flash, allowing for greater accessibility across devices—without sacrificing interactivity.
+
If that’s not impressive enough, these responsive modules also have cleaner navigation, larger font sizes, smaller file sizes, and more intuitive layouts, making on-the-go training easier than ever!
+
And like all of the training materials we create, our responsive modules, designed by our professional instructional designers and e-learning developers, are meticulously crafted to be engaging, memorable learning events catered to specific employee demographics. We always collaborate with subject matter experts (SMEs), managers, and other stakeholders to ensure training meets organizational goals and effectively transforms employees from the inside out.
-Want to find out how responsive m-learning can improve your training program? Send us a message at info@bravolt.com. We’re ready to partner with you!
+
+Want to find out how responsive m-learning can improve your training program? Send us a message at info@bravolt.com. We’re ready to partner with you!
diff --git a/public/blog/songs-of-the-product-owner/index.html b/public/blog/songs-of-the-product-owner/index.html
index a0cf3f4..f08c4cc 100644
--- a/public/blog/songs-of-the-product-owner/index.html
+++ b/public/blog/songs-of-the-product-owner/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -149,17 +149,27 @@ Songs of the Product Owner
- Congrats! You just became a Product Owner. Your knowledge of the industry and the product in development caused the powers that be to put you in charge. Crafting this valuable product is similar to producing a hit song. You want to put out something that is loved by the audience and profitable for the company. You have control over the lyrics, the accompanying music, all the way down to the cover art. As a new Product Owner, you must be wondering, how do I start building the hook, avoid misunderstood lyrics, and deliver a platinum product?
+
+
+Congrats! You just became a Product Owner. Your knowledge of the industry and the product in development caused the powers that be to put you in charge. Crafting this valuable product is similar to producing a hit song. You want to put out something that is loved by the audience and profitable for the company. You have control over the lyrics, the accompanying music, all the way down to the cover art. As a new Product Owner, you must be wondering, how do I start building the hook, avoid misunderstood lyrics, and deliver a platinum product?
+
I Left my Brains Down in Africa
+
Without research into your market, what other competitors are doing, or thinking about the makeup of your key customers, even the best product will fail. Your hit song will fall on deaf ears. Through thoughtful research you can identify opportunities and insights that can help the team compose a hit feature. This goes beyond just asking a customer what they want and might involve prototyping or testing new features against a sample group. Continually researching throughout the life of the product helps prioritize your features and ideas and aids your team’s continuous improvement of the product. Combining your brains and research will ensure your product starts off on the right foot and continues to deliver value.
+
Hold me Closer, Tony Danza
+
All of the research and interactions with the product will lead to a backlog of work that your team will tackle. Composing a song might take a guitar riff and a small lyric written on a napkin, and by collaborating with your bandmates, that input is transformed into a fully formed song. A valuable product takes the same teamwork. You give your team the vision of what they are building and why they are building it. You give them the key elements of the song and help them understand why they are important. You work closely with them to provide clarification about questions the team might have about an element of the product. If a member of your band needs to know what something is supposed to sound like, it is up to you to clarify it. Clarifying saves time and energy and keeps the product moving forward.
-Don’t go, Jason Waterfalls
+
+Don’t go, Jason Waterfalls
+
A song or product develops and changes over time. Releasing a sample of the song or a small feature update to your product keeps the customer engaged and providing feedback. Reacting to this feedback in a timely manner means you need to construct stories for your team to work on that still produce value for the product but are small enough to be delivered in your defined iterations. If you create stories that are too large or all have the same importance, it will be hard for your team to deliver and you will slip into the waterfall versus your desired agile delivery method. Stories will move from iteration to iteration and product delivery and the feedback loop will suffer. Of course there are examples of songs and products that are a hit from the day of release, but a more surefire way to create is through incremental release and feedback. Instead of going over the waterfall, stick to the calmer rivers and lakes of agile.
Good Product Owners have a lot of things to manage and consider to ensure their lyrics are both heard and understood. Make sure that you are not only considering product improvements but how you can improve your own skill set. Take classes, read, and converse with other Product Owners. Good luck in your new role, and now if you will excuse me, I’ve got two chickens to paralyze.
+
Interested in becoming a product owner?
+
Bravo LT is hosting a Certified Srum Product Owner Training on November 21 - 22, 2019.
- Find more information here.
+ Find more information here.
diff --git a/public/blog/we-moved-downtown-grand-rapids/index.html b/public/blog/we-moved-downtown-grand-rapids/index.html
index 49ad216..0af6d47 100644
--- a/public/blog/we-moved-downtown-grand-rapids/index.html
+++ b/public/blog/we-moved-downtown-grand-rapids/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -149,13 +149,22 @@ We Moved Downtown Grand Rapids
- Our New Location#####
+
+
+Our New Location
+
Bravo has officially moved downtown Grand Rapids! Last month we said goodbye to our Cascade Parkway location and (after a few construction projects and a bit of cosmetic work in the new space) hello to Monroe Center. Our official signage is in the works, but in the meantime, we are the dark gray building located at the corner of Division and Monroe Center. More specifically:
+
Bravo LT, 40 Monroe Center NW, Suite 11, Grand Rapids, MI 49503
+
We are thrilled to be downtown, closer to clients and Bravo LT teammates. We love when our team stops by to grab coffee or stays on site to collaborate on projects. It’s also been a bonus to have such great restaurants and coffee shops nearby. If you see anyone with Bravo LT swag out and about at Littlebird, Brick and Porter, Madcap, or The Paper Studio please say hello! We’re pumped our frequent visits support local businesses.
+
The biggest reason for moving downtown was to be immersed within the community. As a software development company, we’re always looking for ways to inspire and give back to the next generation of developers. Just last week we had the opportunity to tour Innovation Central and begin brainstorming ways our developers may be able to offer exploratory Raspberry Pi workshops to Innovation students.
+
If you find yourself walking down Monroe Center, please stop in for coffee or La Croix, to grab some Bravo LT swag, or to simply say hello. We’re thankful to our West Michigan clients, and beyond, for supporting Bravo LT throughout the years; we look forward to continued growth and serving you and your businesses in the future.
+
/////
+
What are some ways you support your local community? Take our #GoLocalMI challenge and show us your favorite West Michigan people, places, and businesses on Twitter or Instagram!
diff --git a/public/blog/what-is-rest/index.html b/public/blog/what-is-rest/index.html
index 1032feb..2367a11 100644
--- a/public/blog/what-is-rest/index.html
+++ b/public/blog/what-is-rest/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -149,41 +149,70 @@ What is REST?
- What is REST?
+
+
+What is REST?
+
Representational State Transfer (REST) is a very popular term in web development today. REST is used to describe many things, kind of like “API”, and because of that I was very confused by the acronym. The goal of this post is to provide a high-level description of REST in hopes that you will navigate away with a better understanding of the architectural style.
-What is RESTful?
-If you have worked on web software, chances are you have been a part of a design discussion where somebody declared, “______ would be more RESTful”. *What does that mean?*
+
+What is RESTful?
+
+If you have worked on web software, chances are you have been a part of a design discussion where somebody declared, “______ would be more RESTful”. What does that mean?
+
It is important to note that REST is a specification not an implementation. In other words, REST defines the requirements for a system but does not define how you should write your code in order to meet those requirements. For example, to say that plural paths (/users
) are more RESTful than singular paths (/user
) is not necessarily true. Instead, it is more correct to say: it is RESTful to choose either plural or singular paths to use across the whole system in order to provide a consistent API. REST does not care what naming convention is used as long as the convention is consistent across all interfaces. This is defined by the Uniform Interface constraint.
+
Constraints are what define the requirements for REST. Once you understand the constraints you can confidently declare what is and is not RESTful.
-Constraints
+
+Constraints
+
There are six architectural constraints that define REST. Their original definition can be found in Roy Fielding’s dissertation, section 5.1 Deriving REST, and I will summarize them here.
-Client-Server
+
+Client-Server
+
The client-server constraint involves separating client concerns from server concerns. Let us simplify an application into two parts: user interface and business logic. Business logic stays the same whether a consumer is using an Apple or an Android phone, but the user interface code can be different. A client-server system separates business logic from the user interface so that server code can be written once and service a variety of clients.
-
-Stateless
+
+
+
+Stateless
+
The stateless constraint can be described as, “stateful client, stateless server” where statelessness is preserved on the server. An example of state is a user’s session. A common way to implement authentication in a web application is to utilize a session, which can look something like this:
+
- User logs in with a username and password. This sends a request from the client to the server with the credentials.
- If successful, the server creates a session and responds to the client with a session ID. The session is managed by the server.
- While the user is logged in, every request must include the session ID. With each request, the server checks the session ID to authenticate the request.
+
This approach is not RESTful because it requires the server to keep track of sessions for each client. It also violates the stateless constraint because each request does not include all of the information needed in order to determine the full nature of the request. A more RESTful approach would look like:
+
- User logs in with a username and password. This sends a request from the client to the server with the credentials.
- If successful, the client saves the username and password. Every request going forward includes the username and password.
- For each request, the server verifies the username and password. No session is managed by the server.
+
There are trade-offs to each approach. A stateful server can be more efficient, a stateless server can be more scalable. Do what makes the most sense for your business case, but know that REST specifically calls for stateless server.
-
-Cache
+
+
+
+Cache
+
Web applications are bound to networks by nature. A consequence of network communication is that it takes time to transfer information through a wire. In order to provide an efficient system, it is important to utilize mechanisms that optimize the amount of information that must be transferred. One optimization strategy is caching, the third constraint of REST.
+
Caching involves storing server data on the client. This enables a client to avoid repetitive requests to the server to retrieve the same data. When implementing a cache, it is important to understand when cached data should expire. Caching is great for increasing performance, but has the consequence of providing out-of-date information if not managed correctly. Seek to implement caches where data rarely changes or where there is a reliable way to refresh the cache when data does change.
+
According to REST, caching should always be done client-side. Note: a server can be both a server and a client!
-
-
+
+
+
+
+
The uniform interface constraint focuses on the manner in which a system communicates itself to other systems or clients. This focuses on the API layer and is described through four sub-constraints.
-Identification of Resources
+
+Identification of Resources
+
Identification of resources deals with how the functionality of an API is described. For an example, let us look at an application that allows creating and managing users. The following functions are available:
+
- Get all users
- Add a new user
@@ -192,28 +221,50 @@ Identification of Resources
- Update a user’s birthday
- Delete a user
-
-
+
+
+Let us examine two potential HTTP API designs:
+
+
+
+
Design A represents what is commonly found in SOAP or other non-REST architectures. Design B is typically preferred in a RESTful design because it identifies operations in a more uniform and predictable manner.
-Manipulation of Resources Through Representations
+
+Manipulation of Resources Through Representations
+
A “representation” can be thought of as a JSON structure. To continue the user example from above, the request to GET /users/{id}
might return a response of:
+
{
“id”: 1,
“name”: “Joseph”
“bday”: “1992/09/25”
}
-
This is the user representation. The manipulation of resources through representations constraint defines that an interface should use consistent structures when referring to the same entity. So when a user is being created, updated, retrieved, or referenced; its structure remains the same.
-Self-Descriptive Messages
+
+
+This is the user representation. The manipulation of resources through representations constraint defines that an interface should use consistent structures when referring to the same entity. So when a user is being created, updated, retrieved, or referenced; its structure remains the same.
+
+Self-Descriptive Messages
+
The self-descriptive messages constraint requires requests and responses to define all of the information needed to understand the purpose of the request/response. In HTTP, this includes the use of headers, status codes, and HTTP methods (GET, PUT, POST, etc).
+
A redirect response is a good example of this constraint. Let us pretend that a client is requesting a user’s profile picture, but the location of the picture has moved. The proper service response would include a 302 HTTP status code (Object Moved) with a Location
header that points to the new location of the picture. Browsers are programmed to automatically handle this interaction so that a user sees no difference. If a server is improperly programmed and the Location
header is missing, the response will not include all of the required information and the user will be left with an incomplete view.
-
+
+
+
HATEOAS sounds complicated, but really what it means is that links (URLs) should be provided by a server to direct clients through available functionality. A way to illustrate this is to think of a homepage. Let us use StackOverflow.com as an example. When a client requests the StackOverflow homepage, there are many routes the user can navigate from there: top posts, profile page, hot questions, etc. It is the server’s job to provide URLs to navigate to each of those available functions. The idea being, the only URL a client should need to know is the root/home page. From there, the client can navigate through the links provided in the server’s response. Another example to think about is a website that mimics a book. If the browser requests page 50 from the server, the server should respond with page 50 and also include links to the previous and next page. This allows the client to navigate throughout the book.
-Layered System
+
+Layered System
+
A layered system architecture is a common best practice found across most software systems. This style is found internal to code bases as well as external across an entire system. Internally this is observed as separating code into controller, service, and database files. Externally this is observed as maintaining business logic on proprietary systems and delegating to 3rd parties for responsibilities like payment processing, for example. Service-oriented architectures fit nicely into this constraint as they require system components to be isolated by responsibility, creating layers between services.
-
-Code-On-Demand
+
+
+
+Code-On-Demand
+
The code-on-demand constraint opens the door for servers to provide packaged code to clients for client convenience. This is useful when a system expects all clients to require similar behavior. If a reservation system is used as an example, it could be expected that many clients will require a date picker in order to select a time period for reservations. Code-on-demand defines that it would be RESTful for that reservation system to provide a pre-built JavaScript widget for selecting dates.
-Summary
+
+Summary
+
Client-server, stateless, cache, uniform interface, layered system, and code-on-demand are the core principles that define REST. Hopefully this article provides some insight into the meaning of this popular acronym. If you are interested in diving deeper into the source of the REST architectural style, its origin can be found in Architectural Styles and the Design of Network-based Software Architectures by Roy Fielding.
diff --git a/public/blog/why-bravo-cares/index.html b/public/blog/why-bravo-cares/index.html
index eaf2d7e..17f3bdb 100644
--- a/public/blog/why-bravo-cares/index.html
+++ b/public/blog/why-bravo-cares/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -150,13 +150,20 @@ Giving Back - Why Bravo Cares
When I first interviewed for my position at Bravo LT a little more than a year ago, my interviewer—Bravo LT president Ed—asked me what some of my hobbies were. “Well, I really like listening to music…I like drawing…oh, and I volunteer at the local animal shelter,” I responded. Ed briefly commended me on my volunteering efforts—a nicety, I assumed—and the conversation moved right along.
+
Fast forward to a few days later when I received an email from Ed telling me I was a good fit for the gig—cool! And part of the reason I was a great fit, he said, was because of my devotion to animal rescue—wow, really? I guess I hadn’t realized during my interview how important that effort was to Bravo LT. Learning this, I knew right then that Bravo was exactly the kind of company I wanted to be a part of.
+
Since my hiring, I’ve learned that Bravo LT has a long history of being deeply involved in the West Michigan community, from offering free educational talks to the public to hosting STEM summer camps for kids, these functions have long been a part of our commitment to donating our time and resources to causes we believe in.
+
However, I noticed there wasn’t a formal name for all of this philanthropic work that we did. That was when my colleague, Ben, and I had an a-ha moment: What if we created an actual program for our charitable endeavors? And why couldn’t we kick it up a notch? So that’s exactly what we did!
+
We launched our Care initiative in the summer of 2016, and since then, I’m pretty proud to say we’ve done a whole lot of good—from sponsoring the Spectrum Health Foundation’s Gala 2016 to holding a supplies drive for the local animal shelter to supporting God’s Kitchen and volunteering at Kids’ Food Basket. We’ve also hosted several educational talks and held another summer camp for kids. How’s that for kicking it up a notch?
+
Thinking back to the day I accepted this job, I remember how excited I was to find out that Bravo LT’s community commitment aligned so closely with my own passion for volunteering. And now that we’ve launched our Care initiative, I’m even more excited—excited to see the program grow, and excited to find new ways we can give back to our amazing community!
-
-Do you know of a great non-profit that could use a little help? Send your ideas to info@bravolt.com —we’d love to hear them!
+
+
+
+Do you know of a great non-profit that could use a little help? Send your ideas to info@bravolt.com —we’d love to hear them!
diff --git a/public/care/index.html b/public/care/index.html
index a3167ca..e6352bb 100644
--- a/public/care/index.html
+++ b/public/care/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/care/index.xml b/public/care/index.xml
index 1f2a3bd..048b18b 100644
--- a/public/care/index.xml
+++ b/public/care/index.xml
@@ -1,4 +1,4 @@
-
+
We Care on Bravo LT | Learning & Technology
diff --git a/public/careers/index.html b/public/careers/index.html
index 8443763..8dddc3a 100644
--- a/public/careers/index.html
+++ b/public/careers/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
@@ -180,6 +180,8 @@ Open Positions
Software Engineer - IoT
+ System Analyst
+
Scrum Master
Agile Business Analyst - Team Lead
diff --git a/public/careers/index.xml b/public/careers/index.xml
index f3fe835..97f239a 100644
--- a/public/careers/index.xml
+++ b/public/careers/index.xml
@@ -1,4 +1,4 @@
-
+
Careers on Bravo LT | Learning & Technology
diff --git a/public/categories/index.html b/public/categories/index.html
index 1b84ddb..9167fdd 100644
--- a/public/categories/index.html
+++ b/public/categories/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/categories/index.xml b/public/categories/index.xml
index e067930..84e5be1 100644
--- a/public/categories/index.xml
+++ b/public/categories/index.xml
@@ -1,4 +1,4 @@
-
+
Categories on Bravo LT | Learning & Technology
diff --git a/public/contact/index.html b/public/contact/index.html
index cc5e0af..577ef0b 100644
--- a/public/contact/index.html
+++ b/public/contact/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/contact/index.xml b/public/contact/index.xml
index faed22d..a13e9b5 100644
--- a/public/contact/index.xml
+++ b/public/contact/index.xml
@@ -1,4 +1,4 @@
-
+
Contact Us on Bravo LT | Learning & Technology
diff --git a/public/csm/index.html b/public/csm/index.html
index 1e101f1..325e8ae 100644
--- a/public/csm/index.html
+++ b/public/csm/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/csm/index.xml b/public/csm/index.xml
index d985fd8..e60311c 100644
--- a/public/csm/index.xml
+++ b/public/csm/index.xml
@@ -1,4 +1,4 @@
-
+
Certified Scrum Master Training on Bravo LT | Learning & Technology
diff --git a/public/cspo/index.html b/public/cspo/index.html
index 21481fb..61ea76e 100644
--- a/public/cspo/index.html
+++ b/public/cspo/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/cspo/index.xml b/public/cspo/index.xml
index f6382de..a3904a2 100644
--- a/public/cspo/index.xml
+++ b/public/cspo/index.xml
@@ -1,4 +1,4 @@
-
+
Certified Scrum Product Owner (CSPO®) Training on Bravo LT | Learning & Technology
diff --git a/public/images/jobdescriptions/21-system-analyst.pdf b/public/images/jobdescriptions/21-system-analyst.pdf
new file mode 100644
index 0000000..2bf9483
Binary files /dev/null and b/public/images/jobdescriptions/21-system-analyst.pdf differ
diff --git a/public/index.html b/public/index.html
index b8ae5f7..7e80d7b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -7,7 +7,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/index.xml b/public/index.xml
index 5e50299..7356471 100644
--- a/public/index.xml
+++ b/public/index.xml
@@ -1,4 +1,4 @@
-
+
Bravo LT | Learning & Technology
@@ -6,7 +6,7 @@
Recent content on Bravo LT | Learning & Technology
Hugo -- gohugo.io
en-us
- Mon, 27 Jan 2020 12:37:52 +0600
+ Thu, 07 Nov 2019 12:11:29 +0600
@@ -26,7 +26,7 @@
Fri, 12 Jul 2019 12:11:29 +0600
//www.bravolt.com/blog/bravos-ribbon-cutting-open-house/
- Save the Date##### You’re invited to Bravo LT’s Ribbon Cutting & Open House Thursday, August 15th, 3pm - 5pm
+ Save the Date You’re invited to Bravo LT’s Ribbon Cutting & Open House Thursday, August 15th, 3pm - 5pm
Join us as we celebrate our recent move downtown. Ribbon Cutting at 4pm.
We can’t wait to meet our neighbors and catch up with clients and friends!
Snacks and drinks provided.
@@ -49,7 +49,7 @@ The free event will be held on August 12 & 13, 2019 at the Bravo office
Wed, 29 May 2019 12:11:29 +0600
//www.bravolt.com/blog/we-moved-downtown-grand-rapids/
- Our New Location##### Bravo has officially moved downtown Grand Rapids! Last month we said goodbye to our Cascade Parkway location and (after a few construction projects and a bit of cosmetic work in the new space) hello to Monroe Center. Our official signage is in the works, but in the meantime, we are the dark gray building located at the corner of Division and Monroe Center. More specifically:
+ Our New Location Bravo has officially moved downtown Grand Rapids! Last month we said goodbye to our Cascade Parkway location and (after a few construction projects and a bit of cosmetic work in the new space) hello to Monroe Center. Our official signage is in the works, but in the meantime, we are the dark gray building located at the corner of Division and Monroe Center. More specifically:
Bravo LT, 40 Monroe Center NW, Suite 11, Grand Rapids, MI 49503
@@ -121,8 +121,7 @@ The free event will be held on September 11 & 12, 2018 at Calvin College
//www.bravolt.com/blog/agile-in-grand-rapids/
Equip Your Teams with Agile We’re passionate about equipping your teams with the cutting-edge Lean Agile Certification training they need!
-This summer we hosted an Agile Advanced Scrum Master Certification course in down-town Grand Rapids. We had a blast hosting and teaching participants from Gordon Food Service, Meijer, Spectrum Health, and more. Most beneficially, great discussions formed across the varying industries which created a learning environment filled with collaboration.
-During the two day certification course we covered such things as:
+This summer we hosted an Agile Advanced Scrum Master Certification course in down-town Grand Rapids. We had a blast hosting and teaching participants from Gordon Food Service, Meijer, Spectrum Health, and more. Most beneficially, great discussions formed across the varying industries which created a learning environment filled with collaboration.
-
@@ -141,8 +140,7 @@ Google brought a lot to the table through their livestream event. Many were amaz
Fri, 22 Sep 2017 12:11:29 +0600
//www.bravolt.com/blog/2017-raspberry-pi-youth-camp-recap/
- At Bravo LT, we believe STEM (Science, Technology, Engineering, Math) education is key to strengthening the future for the next generation. As a technology and e-Learning company in West Michigan, we love introducing the next generation of children to technology. Last month we hosted our third-annual Raspberry Pi Youth Camp which gave local students (ages 10-14) an opportunity to explore computer programming in a fun and engaging way.
- We hosted this free event at Davenport University in Grand Rapids.
+ At Bravo LT, we believe STEM (Science, Technology, Engineering, Math) education is key to strengthening the future for the next generation. As a technology and e-Learning company in West Michigan, we love introducing the next generation of children to technology. Last month we hosted our third-annual Raspberry Pi Youth Camp which gave local students (ages 10-14) an opportunity to explore computer programming in a fun and engaging way.
-
@@ -239,7 +237,8 @@ The free event, held August 9 and 11, 2016 at Davenport University in Grand Rapi
//www.bravolt.com/blog/bravo-open-source-symposium/
Bravo Open Source Symposium October 26, November 2, 9, and 16 5:00 - 7:00 PM
Event Description: BOSS 2015 explores specific open source topics, to include:
- Data Grids: To Infinispan and Beyond (Day 1) Introduction to Docker (Day 2) Maven for Power Users (Day 3) Introduction to Clojure (Day 4) This free four-day symposium, held on alternating Mondays in September and October, features highly interactive training sessions to include the following delivery methods: traditional lecture and demonstration, video, case studies, hands-on activities and group discussion.
+ Data Grids: To Infinispan and Beyond (Day 1) Introduction to Docker (Day 2) Maven for Power Users (Day 3) Introduction to Clojure (Day 4)
+This free four-day symposium, held on alternating Mondays in September and October, features highly interactive training sessions to include the following delivery methods: traditional lecture and demonstration, video, case studies, hands-on activities and group discussion.
diff --git a/public/portfolio/index.html b/public/portfolio/index.html
index 1084caa..f3e85a8 100644
--- a/public/portfolio/index.html
+++ b/public/portfolio/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/portfolio/index.xml b/public/portfolio/index.xml
index 5795f64..fa34755 100644
--- a/public/portfolio/index.xml
+++ b/public/portfolio/index.xml
@@ -1,4 +1,4 @@
-
+
Portfolio on Bravo LT | Learning & Technology
diff --git a/public/rpi/index.html b/public/rpi/index.html
index ad338a6..7e69b62 100644
--- a/public/rpi/index.html
+++ b/public/rpi/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/rpi/index.xml b/public/rpi/index.xml
index b50b64d..9fec0c0 100644
--- a/public/rpi/index.xml
+++ b/public/rpi/index.xml
@@ -1,4 +1,4 @@
-
+
Raspberry Pi on Bravo LT | Learning & Technology
diff --git a/public/service/index.html b/public/service/index.html
index a7e274b..a14d29a 100644
--- a/public/service/index.html
+++ b/public/service/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/service/index.xml b/public/service/index.xml
index d8a6428..6e1701d 100644
--- a/public/service/index.xml
+++ b/public/service/index.xml
@@ -1,4 +1,4 @@
-
+
Our Services on Bravo LT | Learning & Technology
diff --git a/public/sitemap.xml b/public/sitemap.xml
index 9a9cf11..9a96ee8 100644
--- a/public/sitemap.xml
+++ b/public/sitemap.xml
@@ -1,15 +1,15 @@
-
+
- //www.bravolt.com/
+ //www.bravolt.com/rpi/
2020-01-27T12:37:52+06:00
- //www.bravolt.com/rpi/
- 2020-01-27T12:37:52+06:00
+ //www.bravolt.com/
+ 2019-11-07T12:11:29+06:00
diff --git a/public/tags/index.html b/public/tags/index.html
index cf08c44..6acf520 100644
--- a/public/tags/index.html
+++ b/public/tags/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/tags/index.xml b/public/tags/index.xml
index dd9b284..bc29e34 100644
--- a/public/tags/index.xml
+++ b/public/tags/index.xml
@@ -1,4 +1,4 @@
-
+
Tags on Bravo LT | Learning & Technology
diff --git a/public/technology/index.html b/public/technology/index.html
index 1084caa..f3e85a8 100644
--- a/public/technology/index.html
+++ b/public/technology/index.html
@@ -6,7 +6,7 @@
-
+
Bravo LT | Learning & Technology
diff --git a/public/technology/index.xml b/public/technology/index.xml
index a10e498..e91cc5f 100644
--- a/public/technology/index.xml
+++ b/public/technology/index.xml
@@ -1,4 +1,4 @@
-
+
Portfolio on Bravo LT | Learning & Technology
diff --git a/static/images/jobdescriptions/21-system-analyst.pdf b/static/images/jobdescriptions/21-system-analyst.pdf
new file mode 100644
index 0000000..2bf9483
Binary files /dev/null and b/static/images/jobdescriptions/21-system-analyst.pdf differ