There are two parts to this question. An alternative way of determining the shortest string is the old-fashioned sub-query:
select distinct ColumnA
from tablea
where length(ColumnA) = ( select min(length(ColumnA))
from TableA
where ColumnB = 'XXX'
)
/
Which is better? It depends on the indexing, data volumes, etc but I would guess your version is likely to perform better. It might also give slightly different results, unless you duplicated the where ColumnB = 'XXX' in the outer query.
Like your solution this query will return one row for each value of ColumnA which is three characters long. If you want to return a single row you can do so by restricting it with rownum. It you want to apply some criterion to determine which is the first row you need to embed it in a further outer query (using my query but a variant on yours would work too) ...
select * from (
select ColumnA
from tablea
where length(ColumnA) = ( select min(length(ColumnA))
from TableA
where ColumnB = 'XXX'
)
order by ColumnA
)
where rownum = 1
/