In your example it’s not obvious what the difference is between keepempty=false and true because the empty fields occur after the 4th split and you have limit=4. Removing this we can see:
julia> split("hello;there;user;;;foo", ";"; keepempty=true)
6-element Vector{SubString{String}}:
"hello"
"there"
"user"
""
""
"foo"
julia> split("hello;there;user;;;foo", ";"; keepempty=false)
4-element Vector{SubString{String}}:
"hello"
"there"
"user"
"foo"
Essentially, if you have two delimiters in a row (ie “;;”) it controls whether to return an empty string or to ignore it.
HTH