collect is an aggregation function. The base for the aggregation is everything listed in the with not being an aggregation. If your first query there are no other columns, so the base is null, in the second query you have e as non-aggregating expression.
Therefore the first one will do a global aggregation wheres the second query aggregates per e.
I guess related engineers are engineers having a relationship to the same project
MATCH (e1:Engineer)<-[:BELONGS_TO]-(p:Project)-[:BELONGS_TO]->(e2:Engineer)
WHERE id(e1) < id(e2) // trick to prevent e1 and e2 from changing roles causing duplication of result set
RETURN e1.name, e2.name, count(*) AS numberOfCommonProjects
If you want a list of engineers per project:
MATCH (p:Project)-[:BELONGS_TO]->(e:Engineer)
RETURN p.name, collect(e.name)
If you have projects without engineers but wanted them listed as well:
MATCH (p:Project)
OPTIONAL MATCH (p)-[:BELONGS_TO]->(e:Engineer)
RETURN p.name, collect(e.name)